From 9e73992ee354659b2fe63ce55c14d81ebf8b87ad Mon Sep 17 00:00:00 2001 From: Kim Dong Hyun <107387817+doggopawer@users.noreply.github.com> Date: Fri, 1 Dec 2023 14:15:25 +0900 Subject: [PATCH 1/2] =?UTF-8?q?:tada:=20=EA=B0=81=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20ErrorBoundary,=20Suspense=20=EC=A0=81=EC=9A=A9=20(#?= =?UTF-8?q?145)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :tada: 각 리스트 ErrorBoundary, Suspense 적용 * :tada: NoData UI 버그 해결 - 탭과 isEmpty fallback 이 겹치는 현상 해결 --- .../card-list-content/CardListContent.tsx | 60 ++---------- .../cards/components/card-list/CardList.tsx | 54 ++++++++--- .../MyCardListContent.tsx | 51 +++------- .../my/components/my-card-list/MyCardList.tsx | 51 ++++++++-- .../ChatRoomListContent.tsx | 50 +++------- .../chat-room-list/ChatRoomList.tsx | 73 ++++++++++----- .../(routes)/dibs/components/MyDibsList.tsx | 48 ++++++++-- .../dibs/components/MyDibsTemplate.tsx | 51 +++------- .../MyTradeHistoryListContent.tsx | 50 +++------- .../MyTradeHistoryList.tsx | 77 +++++++++++---- .../NotificationListContent.tsx | 52 +++-------- .../notification-list/NotificationList.tsx | 80 +++++++++++----- .../NotificationReadButton.tsx | 13 +-- .../NotificationStatusManager.tsx | 21 +++++ .../notification-status-manager/index.tsx | 3 + .../NotificationStatusTabs.tsx | 5 +- .../MySuggestionListContent.tsx | 52 +++-------- .../my-suggestion-list/MySuggestionList.tsx | 93 ++++++++++++++----- .../empty-data-wrapper/EmptyDataWrapper.tsx | 20 ++++ .../domain/empty-data-wrapper/index.tsx | 3 + .../exception-boundary/ExceptionBoundary.tsx | 39 -------- .../domain/exception-boundary/index.tsx | 3 - src/components/domain/no-data/NoData.tsx | 17 +++- src/hooks/api/queries/useCardsQuery.ts | 1 - 24 files changed, 507 insertions(+), 460 deletions(-) create mode 100644 src/app/(root)/(routes)/notifications/components/notification-status-manager/NotificationStatusManager.tsx create mode 100644 src/app/(root)/(routes)/notifications/components/notification-status-manager/index.tsx create mode 100644 src/components/domain/empty-data-wrapper/EmptyDataWrapper.tsx create mode 100644 src/components/domain/empty-data-wrapper/index.tsx delete mode 100644 src/components/domain/exception-boundary/ExceptionBoundary.tsx delete mode 100644 src/components/domain/exception-boundary/index.tsx diff --git a/src/app/(root)/(routes)/cards/components/card-list-content/CardListContent.tsx b/src/app/(root)/(routes)/cards/components/card-list-content/CardListContent.tsx index 9411a23e..f8429750 100644 --- a/src/app/(root)/(routes)/cards/components/card-list-content/CardListContent.tsx +++ b/src/app/(root)/(routes)/cards/components/card-list-content/CardListContent.tsx @@ -1,65 +1,25 @@ 'use client' -import { useEffect, useRef } from 'react' +import { Suspense } from 'react' import { ErrorBoundary } from 'react-error-boundary' -import { useSearchParams } from 'next/navigation' -import ExceptionBoundary from '@/components/domain/exception-boundary' -import { useCardsQuery } from '@/hooks/api/queries/useCardsQuery' -import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' -import { CategoryObjs, PriceRangeObjs } from '@/types/card' +import Loading from '@/app/loading' +import DefaultErrorTemplate from '@/components/domain/errors/DefaultErrorTemplate' import CardFilterSection from '../card-filter-section' import CardList from '../card-list/CardList' const CardListContent = () => { - const searchParams = useSearchParams() - - // TODO: 현재 API 명세에 status에 어떤 값을 줘야하는지에 대한 정의가 되어 있지 않기 때문에 임시로 상수 값을 전달함 => 추후에 실제 동작 값으로 고치기 - const { - data, - fetchNextPage, - isError, - isFetchingNextPage, - isLoading, - hasNextPage, - } = useCardsQuery({ - category: - (searchParams.get('category') as CategoryObjs['key']) || undefined, - priceRange: - (searchParams.get('priceRange') as PriceRangeObjs['key']) || undefined, - cardTitle: searchParams.get('cardTitle' as string) || '', - }) - - const lastElementRef = useRef(null) - const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) - - useEffect(() => { - if (isFetchingNextPage || !hasNextPage) { - return - } - - if (entry?.isIntersecting) { - fetchNextPage() - } - }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage, hasNextPage]) - - // TODO: 아이템이 없을시 어떤 UI를 보여줄지 차후에 결정 - - const isEmpty = data?.pages[0].data.cardList.length === 0 - return ( <> - console.log('재시도')} /> + } > - 렌더링 중 문제가 발생했습니다.}> + }> - - -
+ + ) } diff --git a/src/app/(root)/(routes)/cards/components/card-list/CardList.tsx b/src/app/(root)/(routes)/cards/components/card-list/CardList.tsx index a0c872a4..29d8a413 100644 --- a/src/app/(root)/(routes)/cards/components/card-list/CardList.tsx +++ b/src/app/(root)/(routes)/cards/components/card-list/CardList.tsx @@ -1,23 +1,54 @@ -import { Fragment } from 'react' -import { useSearchParams } from 'next/navigation' +import { Fragment, useEffect, useRef } from 'react' +import { useRouter, useSearchParams } from 'next/navigation' import TradeStatusCard from '@/components/domain/card/trade-status-card' +import EmptyDataWrapper from '@/components/domain/empty-data-wrapper' +import NoData from '@/components/domain/no-data' +import AppPath from '@/config/appPath' import { useCardsQuery } from '@/hooks/api/queries/useCardsQuery' +import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' import { GetCardListRes } from '@/services/card/card' import { Card, CategoryObjs, PriceRangeObjs } from '@/types/card' const CardList = () => { const searchParams = useSearchParams() + const router = useRouter() - const { data } = useCardsQuery({ - category: - (searchParams.get('category') as CategoryObjs['key']) || undefined, - priceRange: - (searchParams.get('priceRange') as PriceRangeObjs['key']) || undefined, - cardTitle: searchParams.get('cardTitle' as string) || '', - }) + // TODO: 현재 API 명세에 status에 어떤 값을 줘야하는지에 대한 정의가 되어 있지 않기 때문에 임시로 상수 값을 전달함 => 추후에 실제 동작 값으로 고치기 + const { data, fetchNextPage, isFetchingNextPage, hasNextPage } = + useCardsQuery({ + category: + (searchParams.get('category') as CategoryObjs['key']) || undefined, + priceRange: + (searchParams.get('priceRange') as PriceRangeObjs['key']) || undefined, + cardTitle: searchParams.get('cardTitle' as string) || '', + }) + + const lastElementRef = useRef(null) + const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) + + useEffect(() => { + if (isFetchingNextPage || !hasNextPage) { + return + } + + if (entry?.isIntersecting) { + fetchNextPage() + } + }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage, hasNextPage]) + + const isEmpty = data?.pages[0].data.cardList.length === 0 return ( - <> + router.push(AppPath.newCard())} + /> + } + > {data?.pages.map(({ data: { cardList } }: GetCardListRes, pageIndex) => ( {cardList.map((card: Card) => ( @@ -27,7 +58,8 @@ const CardList = () => { ))} ))} - +
+ ) } diff --git a/src/app/(root)/(routes)/cards/my/components/my-card-list-content/MyCardListContent.tsx b/src/app/(root)/(routes)/cards/my/components/my-card-list-content/MyCardListContent.tsx index 39f7393c..63427186 100644 --- a/src/app/(root)/(routes)/cards/my/components/my-card-list-content/MyCardListContent.tsx +++ b/src/app/(root)/(routes)/cards/my/components/my-card-list-content/MyCardListContent.tsx @@ -1,10 +1,9 @@ 'use client' -import { useEffect, useRef, useState } from 'react' +import { Suspense, useState } from 'react' import { ErrorBoundary } from 'react-error-boundary' -import ExceptionBoundary from '@/components/domain/exception-boundary' -import { useMyCardsQuery } from '@/hooks/api/queries/useMyCardsQuery' -import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' +import Loading from '@/app/loading' +import DefaultErrorTemplate from '@/components/domain/errors/DefaultErrorTemplate' import { TradeStatus } from '@/types/card' import MyCardList from '../my-card-list/MyCardList' import TradeStatusTabs from '../trade-status-tabs' @@ -12,49 +11,21 @@ import TradeStatusTabs from '../trade-status-tabs' const MyCardListContent = () => { const [tradeStatus, setTradeStatus] = useState('TRADE_AVAILABLE') - const { - data, - fetchNextPage, - isLoading, - isError, - isFetchingNextPage, - hasNextPage, - } = useMyCardsQuery({ - tradeStatus, - }) - - const lastElementRef = useRef(null) - const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) - - useEffect(() => { - if (isFetchingNextPage || !hasNextPage) { - return - } - - if (entry?.isIntersecting) { - fetchNextPage() - } - }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage, hasNextPage]) - - const isEmpty = data?.pages[0].data.cardList.length === 0 return ( <> - console.log('재시도')} /> + } > - 렌더링 중 문제가 발생했습니다.
}> - - - - -
+ }> + + + ) } diff --git a/src/app/(root)/(routes)/cards/my/components/my-card-list/MyCardList.tsx b/src/app/(root)/(routes)/cards/my/components/my-card-list/MyCardList.tsx index b0cb4cc4..d75cec41 100644 --- a/src/app/(root)/(routes)/cards/my/components/my-card-list/MyCardList.tsx +++ b/src/app/(root)/(routes)/cards/my/components/my-card-list/MyCardList.tsx @@ -1,16 +1,48 @@ -import { Fragment } from 'react' -import { InfiniteData } from '@tanstack/react-query' +import { Fragment, useEffect, useRef } from 'react' +import { useRouter } from 'next/navigation' +import EmptyDataWrapper from '@/components/domain/empty-data-wrapper/EmptyDataWrapper' +import NoData from '@/components/domain/no-data' +import AppPath from '@/config/appPath' +import { useMyCardsQuery } from '@/hooks/api/queries/useMyCardsQuery' +import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' import { GetMyCardListRes } from '@/services/card/card' import { Card } from '@/types/card' import MyCard from '../my-card' -const MyCardList = ({ - data, -}: { - data: InfiniteData | undefined -}) => { +const MyCardList = ({ tradeStatus }: { tradeStatus: any }) => { + const router = useRouter() + const { data, fetchNextPage, isFetchingNextPage, hasNextPage } = + useMyCardsQuery({ + tradeStatus, + }) + + const lastElementRef = useRef(null) + const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) + + useEffect(() => { + if (isFetchingNextPage || !hasNextPage) { + return + } + + if (entry?.isIntersecting) { + fetchNextPage() + } + }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage, hasNextPage]) + const isEmpty = data?.pages[0].data.cardList.length === 0 return ( - <> + router.push(AppPath.newCard())} + position={false} + /> + ) + } + > {data?.pages.map( ({ data: { cardList } }: GetMyCardListRes, pageIndex) => ( @@ -20,7 +52,8 @@ const MyCardList = ({ ), )} - +
+ ) } diff --git a/src/app/(root)/(routes)/chatrooms/components/chat-room-list-content/ChatRoomListContent.tsx b/src/app/(root)/(routes)/chatrooms/components/chat-room-list-content/ChatRoomListContent.tsx index fd9c6277..7f53b8a0 100644 --- a/src/app/(root)/(routes)/chatrooms/components/chat-room-list-content/ChatRoomListContent.tsx +++ b/src/app/(root)/(routes)/chatrooms/components/chat-room-list-content/ChatRoomListContent.tsx @@ -1,51 +1,23 @@ 'use client' -import { useEffect, useRef, Fragment } from 'react' +import { Suspense } from 'react' import { ErrorBoundary } from 'react-error-boundary' -import ExceptionBoundary from '@/components/domain/exception-boundary' -import { useChatRoomsQuery } from '@/hooks/api/queries/useChatRoomsQuery' -import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' +import Loading from '@/app/loading' +import DefaultErrorTemplate from '@/components/domain/errors/DefaultErrorTemplate' import ChatRoomList from '../chat-room-list' const ChatRoomListContent = () => { - const { - data, - fetchNextPage, - isLoading, - isError, - isFetchingNextPage, - hasNextPage, - } = useChatRoomsQuery() - - const lastElementRef = useRef(null) - const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) - - useEffect(() => { - if (isFetchingNextPage || !hasNextPage) { - return - } - - if (entry?.isIntersecting) { - fetchNextPage() - } - }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage, hasNextPage]) - - const isEmpty = data?.pages[0].data.chatRoomList.length === 0 - return ( <> - console.log('재시도')} /> + } > - 렌더링 중 문제가 발생했습니다.
}> - - - - -
+ }> + + + ) } diff --git a/src/app/(root)/(routes)/chatrooms/components/chat-room-list/ChatRoomList.tsx b/src/app/(root)/(routes)/chatrooms/components/chat-room-list/ChatRoomList.tsx index 5ce4b73a..3613c972 100644 --- a/src/app/(root)/(routes)/chatrooms/components/chat-room-list/ChatRoomList.tsx +++ b/src/app/(root)/(routes)/chatrooms/components/chat-room-list/ChatRoomList.tsx @@ -1,26 +1,57 @@ -import { Fragment } from 'react' -import { InfiniteData } from '@tanstack/react-query' +import { Fragment, useEffect, useRef } from 'react' +import { useRouter } from 'next/navigation' +import EmptyDataWrapper from '@/components/domain/empty-data-wrapper' +import NoData from '@/components/domain/no-data' +import AppPath from '@/config/appPath' +import { useChatRoomsQuery } from '@/hooks/api/queries/useChatRoomsQuery' +import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' import { GetChatRoomListRes } from '@/services/chat-room/chatRoom' import ChatRoomCard from '../chat-room-card' -const ChatRoomList = ({ - data, -}: { - data: InfiniteData | undefined -}) => ( - <> - {data?.pages.map( - ({ data: { chatRoomList } }: GetChatRoomListRes, pageIndex) => ( - - {chatRoomList.map((chatRoom: any) => ( -
- -
- ))} -
- ), - )} - -) +const ChatRoomList = () => { + const router = useRouter() + const { data, fetchNextPage, isFetchingNextPage, hasNextPage } = + useChatRoomsQuery() + + const lastElementRef = useRef(null) + const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) + + useEffect(() => { + if (isFetchingNextPage || !hasNextPage) { + return + } + + if (entry?.isIntersecting) { + fetchNextPage() + } + }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage, hasNextPage]) + + const isEmpty = data?.pages[0].data.chatRoomList.length === 0 + return ( + router.push(AppPath.home())} + /> + } + > + {data?.pages.map( + ({ data: { chatRoomList } }: GetChatRoomListRes, pageIndex) => ( + + {chatRoomList.map((chatRoom: any) => ( +
+ +
+ ))} +
+ ), + )} +
+ + ) +} export default ChatRoomList diff --git a/src/app/(root)/(routes)/dibs/components/MyDibsList.tsx b/src/app/(root)/(routes)/dibs/components/MyDibsList.tsx index 8a13c064..4800ca91 100644 --- a/src/app/(root)/(routes)/dibs/components/MyDibsList.tsx +++ b/src/app/(root)/(routes)/dibs/components/MyDibsList.tsx @@ -1,16 +1,45 @@ -import { Fragment } from 'react' -import { InfiniteData } from '@tanstack/react-query' +import { Fragment, useEffect, useRef } from 'react' +import { useRouter } from 'next/navigation' import TradeStatusCard from '@/components/domain/card/trade-status-card' +import EmptyDataWrapper from '@/components/domain/empty-data-wrapper' +import NoData from '@/components/domain/no-data' +import AppPath from '@/config/appPath' +import { useMyDibsQuery } from '@/hooks/api/queries/useMyDibsQuery' +import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' import { GetMyDibsRes } from '@/services/card/card' import { Card } from '@/types/card' -const MyDibsList = ({ - data, -}: { - data: InfiniteData | undefined -}) => { +const MyDibsList = () => { + const router = useRouter() + const { data, fetchNextPage, isFetchingNextPage, hasNextPage } = + useMyDibsQuery() + + const lastElementRef = useRef(null) + const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) + + useEffect(() => { + console.log(hasNextPage) + if (isFetchingNextPage || !hasNextPage) { + return + } + + if (entry?.isIntersecting) { + fetchNextPage() + } + }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage, hasNextPage]) + + const isEmpty = data?.pages[0].data.dibList.length === 0 return ( - <> + router.push(AppPath.cards())} + /> + } + > {data?.pages.map(({ data: { dibList } }: GetMyDibsRes, pageIndex) => ( {dibList.map((card: Card) => ( @@ -20,7 +49,8 @@ const MyDibsList = ({ ))} ))} - +
+ ) } export default MyDibsList diff --git a/src/app/(root)/(routes)/dibs/components/MyDibsTemplate.tsx b/src/app/(root)/(routes)/dibs/components/MyDibsTemplate.tsx index 758f5196..d732752d 100644 --- a/src/app/(root)/(routes)/dibs/components/MyDibsTemplate.tsx +++ b/src/app/(root)/(routes)/dibs/components/MyDibsTemplate.tsx @@ -1,52 +1,23 @@ 'use client' -import { useEffect, useRef } from 'react' +import { Suspense } from 'react' import { ErrorBoundary } from 'react-error-boundary' -import ExceptionBoundary from '@/components/domain/exception-boundary' -import { useMyDibsQuery } from '@/hooks/api/queries/useMyDibsQuery' -import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' +import Loading from '@/app/loading' +import DefaultErrorTemplate from '@/components/domain/errors/DefaultErrorTemplate' import MyDibsList from './MyDibsList' const MyDibsTemplate = () => { - const { - data, - fetchNextPage, - isLoading, - isError, - isFetchingNextPage, - hasNextPage, - } = useMyDibsQuery() - - const lastElementRef = useRef(null) - const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) - - useEffect(() => { - console.log(hasNextPage) - if (isFetchingNextPage || !hasNextPage) { - return - } - - if (entry?.isIntersecting) { - fetchNextPage() - } - }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage, hasNextPage]) - - const isEmpty = data?.pages[0].data.dibList.length === 0 - return ( <> - console.log('재시도')} /> + } > - 렌더링 중 문제가 발생했습니다.
}> - - - - -
+ }> + + + ) } diff --git a/src/app/(root)/(routes)/history/components/my-trade-history-list-content/MyTradeHistoryListContent.tsx b/src/app/(root)/(routes)/history/components/my-trade-history-list-content/MyTradeHistoryListContent.tsx index 96bd21bf..915e388e 100644 --- a/src/app/(root)/(routes)/history/components/my-trade-history-list-content/MyTradeHistoryListContent.tsx +++ b/src/app/(root)/(routes)/history/components/my-trade-history-list-content/MyTradeHistoryListContent.tsx @@ -1,51 +1,23 @@ 'use client' -import { useEffect, useRef, Fragment } from 'react' +import { Suspense } from 'react' import { ErrorBoundary } from 'react-error-boundary' -import ExceptionBoundary from '@/components/domain/exception-boundary' -import { useMyTradeHistoryQuery } from '@/hooks/api/queries/useMyTradeHistoriesQuery' -import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' +import Loading from '@/app/loading' +import DefaultErrorTemplate from '@/components/domain/errors/DefaultErrorTemplate' import MyTradeHistoryList from '../my-trade-history-list/MyTradeHistoryList' const MyTradeHistoryListContent = () => { - const { - data, - fetchNextPage, - isLoading, - isError, - isFetchingNextPage, - hasNextPage, - } = useMyTradeHistoryQuery() - - const lastElementRef = useRef(null) - const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) - - useEffect(() => { - if (isFetchingNextPage || !hasNextPage) { - return - } - - if (entry?.isIntersecting) { - fetchNextPage() - } - }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage, hasNextPage]) - - const isEmpty = data?.pages[0].data.historyList.length === 0 - return ( <> - console.log('재시도')} /> + } > - 렌더링 중 문제가 발생했습니다.
}> - - - - -
+ }> + + + ) } diff --git a/src/app/(root)/(routes)/history/components/my-trade-history-list/MyTradeHistoryList.tsx b/src/app/(root)/(routes)/history/components/my-trade-history-list/MyTradeHistoryList.tsx index 3395a5d8..c7977e98 100644 --- a/src/app/(root)/(routes)/history/components/my-trade-history-list/MyTradeHistoryList.tsx +++ b/src/app/(root)/(routes)/history/components/my-trade-history-list/MyTradeHistoryList.tsx @@ -1,27 +1,64 @@ -import { Fragment } from 'react' +import { Fragment, useEffect, useRef } from 'react' import { InfiniteData } from '@tanstack/react-query' +import { useRouter } from 'next/navigation' import TradeHistoryCard from '@/components/domain/card/trade-history-card' +import EmptyDataWrapper from '@/components/domain/empty-data-wrapper' +import NoData from '@/components/domain/no-data' +import AppPath from '@/config/appPath' +import { useMyTradeHistoryQuery } from '@/hooks/api/queries/useMyTradeHistoriesQuery' +import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' 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) => ( -
- -
- ))} -
- ), - )} - -) +const MyTradeHistoryList = () => { + const router = useRouter() + const { + data, + fetchNextPage, + + isFetchingNextPage, + hasNextPage, + } = useMyTradeHistoryQuery() + + const lastElementRef = useRef(null) + const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) + + useEffect(() => { + if (isFetchingNextPage || !hasNextPage) { + return + } + + if (entry?.isIntersecting) { + fetchNextPage() + } + }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage, hasNextPage]) + + const isEmpty = data?.pages[0].data.historyList.length === 0 + return ( + router.push(AppPath.home())} + /> + } + > + {data?.pages.map( + ({ data: { historyList } }: GetMyTradeHistoryListRes, pageIndex) => ( + + {historyList.map((myHistory: TradeHistory) => ( +
+ +
+ ))} +
+ ), + )} +
+ + ) +} export default MyTradeHistoryList diff --git a/src/app/(root)/(routes)/notifications/components/notification-list-content/NotificationListContent.tsx b/src/app/(root)/(routes)/notifications/components/notification-list-content/NotificationListContent.tsx index 02e81654..143ec869 100644 --- a/src/app/(root)/(routes)/notifications/components/notification-list-content/NotificationListContent.tsx +++ b/src/app/(root)/(routes)/notifications/components/notification-list-content/NotificationListContent.tsx @@ -1,10 +1,9 @@ 'use client' -import { useEffect, useRef, useState } from 'react' +import { Suspense, useState } from 'react' import { ErrorBoundary } from 'react-error-boundary' -import ExceptionBoundary from '@/components/domain/exception-boundary' -import { useNotificationsQuery } from '@/hooks/api/queries/useNotificationsQuery' -import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' +import Loading from '@/app/loading' +import DefaultErrorTemplate from '@/components/domain/errors/DefaultErrorTemplate' import NotificationList from '../notification-list' import NotificationReadButton from '../notification-read-button' import NotificationStatusTabs from '../notification-status-tabs' @@ -12,45 +11,20 @@ import NotificationStatusTabs from '../notification-status-tabs' const NotificationListContent = () => { const [isRead, setIsRead] = useState(false) - const { - data, - fetchNextPage, - isLoading, - isError, - isFetchingNextPage, - hasNextPage, - } = useNotificationsQuery(isRead) - - const lastElementRef = useRef(null) - const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) - - useEffect(() => { - if (isFetchingNextPage || !hasNextPage) { - return - } - - if (entry?.isIntersecting) { - fetchNextPage() - } - }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage, hasNextPage]) - - const isEmpty = data?.pages[0].data.notificationList.length === 0 - return ( <> - + - console.log('재시도')} /> + } > - 렌더링 중 문제가 발생했습니다.
}> - - - -
+ }> + + + ) } diff --git a/src/app/(root)/(routes)/notifications/components/notification-list/NotificationList.tsx b/src/app/(root)/(routes)/notifications/components/notification-list/NotificationList.tsx index 0423f3c1..c04c771a 100644 --- a/src/app/(root)/(routes)/notifications/components/notification-list/NotificationList.tsx +++ b/src/app/(root)/(routes)/notifications/components/notification-list/NotificationList.tsx @@ -1,27 +1,65 @@ -import { Fragment } from 'react' -import { InfiniteData } from '@tanstack/react-query' +import { Fragment, useEffect, useRef } from 'react' +import { useRouter } from 'next/navigation' +import EmptyDataWrapper from '@/components/domain/empty-data-wrapper' +import NoData from '@/components/domain/no-data' +import AppPath from '@/config/appPath' +import { useNotificationsQuery } from '@/hooks/api/queries/useNotificationsQuery' +import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' import { GetNotificationListRes } from '@/services/notification/notification' import { Notification } from '@/types/notification' import NotificationCard from '../notification-card' -const NotificationList = ({ - data, -}: { - data: InfiniteData | undefined -}) => ( - <> - {data?.pages.map( - ({ data: { notificationList } }: GetNotificationListRes, pageIndex) => ( - - {notificationList.map((notification: Notification) => ( -
- -
- ))} -
- ), - )} - -) +type NotificationListProps = { + isRead: boolean +} + +const NotificationList = ({ isRead }: NotificationListProps) => { + const router = useRouter() + const { data, fetchNextPage, isFetchingNextPage, hasNextPage } = + useNotificationsQuery(isRead) + + const lastElementRef = useRef(null) + const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) + + useEffect(() => { + if (isFetchingNextPage || !hasNextPage) { + return + } + + if (entry?.isIntersecting) { + fetchNextPage() + } + }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage, hasNextPage]) + + const isEmpty = data?.pages[0].data.notificationList.length === 0 + return ( + router.push(AppPath.newCard())} + position={false} + /> + ) + } + > + {data?.pages.map( + ({ data: { notificationList } }: GetNotificationListRes, pageIndex) => ( + + {notificationList.map((notification: Notification) => ( +
+ +
+ ))} +
+ ), + )} +
+ + ) +} export default NotificationList diff --git a/src/app/(root)/(routes)/notifications/components/notification-read-button/NotificationReadButton.tsx b/src/app/(root)/(routes)/notifications/components/notification-read-button/NotificationReadButton.tsx index 218f1a12..2d0238a3 100644 --- a/src/app/(root)/(routes)/notifications/components/notification-read-button/NotificationReadButton.tsx +++ b/src/app/(root)/(routes)/notifications/components/notification-read-button/NotificationReadButton.tsx @@ -1,15 +1,13 @@ import Button from '@/components/ui/button' import { useNotificationListUpdateMutation } from '@/hooks/api/mutations/useNotificationListUpdateMutation' +import { useNotificationsQuery } from '@/hooks/api/queries/useNotificationsQuery' type NotificationReadButtonProps = { isRead: boolean - isEmpty: boolean } -const NotificationReadButton = ({ - isEmpty, - isRead, -}: NotificationReadButtonProps) => { +const NotificationReadButton = ({ isRead }: NotificationReadButtonProps) => { + const { data } = useNotificationsQuery(false) const { mutate } = useNotificationListUpdateMutation() const handleReadAllNotifications = () => { @@ -17,9 +15,7 @@ const NotificationReadButton = ({ window.location.reload() } - if (isRead) { - return null - } + const isEmpty = data?.pages[0].data.notificationList.length === 0 return (
@@ -28,6 +24,7 @@ const NotificationReadButton = ({ rounded={'lg'} onClick={handleReadAllNotifications} disabled={isEmpty} + style={{ visibility: isRead ? 'hidden' : 'visible' }} > 모두 읽음 처리 diff --git a/src/app/(root)/(routes)/notifications/components/notification-status-manager/NotificationStatusManager.tsx b/src/app/(root)/(routes)/notifications/components/notification-status-manager/NotificationStatusManager.tsx new file mode 100644 index 00000000..d481d1c9 --- /dev/null +++ b/src/app/(root)/(routes)/notifications/components/notification-status-manager/NotificationStatusManager.tsx @@ -0,0 +1,21 @@ +import NotificationReadButton from '../notification-read-button' +import NotificationStatusTabs from '../notification-status-tabs' + +type NotificationStatusManagerProps = { + isRead: boolean + setIsRead: (_isRead: boolean) => void +} + +const NotificationStatusManager = ({ + isRead, + setIsRead, +}: NotificationStatusManagerProps) => { + return ( +
+ + +
+ ) +} + +export default NotificationStatusManager diff --git a/src/app/(root)/(routes)/notifications/components/notification-status-manager/index.tsx b/src/app/(root)/(routes)/notifications/components/notification-status-manager/index.tsx new file mode 100644 index 00000000..b302e254 --- /dev/null +++ b/src/app/(root)/(routes)/notifications/components/notification-status-manager/index.tsx @@ -0,0 +1,3 @@ +import NotificationStatusManager from './NotificationStatusManager' + +export default NotificationStatusManager diff --git a/src/app/(root)/(routes)/notifications/components/notification-status-tabs/NotificationStatusTabs.tsx b/src/app/(root)/(routes)/notifications/components/notification-status-tabs/NotificationStatusTabs.tsx index 5e0f09f9..20650d6d 100644 --- a/src/app/(root)/(routes)/notifications/components/notification-status-tabs/NotificationStatusTabs.tsx +++ b/src/app/(root)/(routes)/notifications/components/notification-status-tabs/NotificationStatusTabs.tsx @@ -10,10 +10,7 @@ const NotificationStatusTabs = ({ setIsRead }: NotificationStatusTabsProps) => { } return ( - + { useState('OFFER') const [directionTypeState, setDirectionTypeState] = useState('RECEIVE') - const { myCardId } = useParams() - - const { - data, - fetchNextPage, - isLoading, - isError, - isFetchingNextPage, - hasNextPage, - } = useMySuggestionsQuery(suggestionTypeState, directionTypeState, myCardId) - - const lastElementRef = useRef(null) - const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) - - useEffect(() => { - if (isFetchingNextPage || !hasNextPage) { - return - } - - if (entry?.isIntersecting) { - fetchNextPage() - } - }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage, hasNextPage]) - - const isEmpty = data?.pages[0].data.suggestionList.length === 0 return ( <> - console.log('재시도')} /> + } > - 렌더링 중 문제가 발생했습니다.
}> + }> - - - -
+ + ) } diff --git a/src/app/(root)/(routes)/suggestions/[myCardId]/components/my-suggestion-list/MySuggestionList.tsx b/src/app/(root)/(routes)/suggestions/[myCardId]/components/my-suggestion-list/MySuggestionList.tsx index 0821787d..54db4998 100644 --- a/src/app/(root)/(routes)/suggestions/[myCardId]/components/my-suggestion-list/MySuggestionList.tsx +++ b/src/app/(root)/(routes)/suggestions/[myCardId]/components/my-suggestion-list/MySuggestionList.tsx @@ -1,37 +1,82 @@ -import { Fragment } from 'react' -import { InfiniteData } from '@tanstack/react-query' +import { Fragment, useEffect, useRef } from 'react' +import { useParams, useRouter } from 'next/navigation' +import EmptyDataWrapper from '@/components/domain/empty-data-wrapper' +import NoData from '@/components/domain/no-data' +import AppPath from '@/config/appPath' +import { useMySuggestionsQuery } from '@/hooks/api/queries/useMySuggestionsQuery' +import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' 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 }) => ( - - ), - )} - - ), - )} - -) +}) => { + const router = useRouter() + const { myCardId } = useParams() + + const { + data, + fetchNextPage, + + isFetchingNextPage, + hasNextPage, + } = useMySuggestionsQuery(suggestionTypeState, directionTypeState, myCardId) + + const lastElementRef = useRef(null) + const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) + + useEffect(() => { + if (isFetchingNextPage || !hasNextPage) { + return + } + + if (entry?.isIntersecting) { + fetchNextPage() + } + }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage, hasNextPage]) + + const isEmpty = data?.pages[0].data.suggestionList.length === 0 + + return ( + router.push(AppPath.cards())} + position={false} + /> + } + > + {data?.pages.map( + ({ data: { suggestionList } }: GetMySuggestionListRes, pageIndex) => ( + + {suggestionList.map( + (mySuggestion: { + cardInfo: Card + suggestionInfo: Suggestion + }) => ( + + ), + )} + + ), + )} +
+ + ) +} export default MySuggestionList diff --git a/src/components/domain/empty-data-wrapper/EmptyDataWrapper.tsx b/src/components/domain/empty-data-wrapper/EmptyDataWrapper.tsx new file mode 100644 index 00000000..3d3f405f --- /dev/null +++ b/src/components/domain/empty-data-wrapper/EmptyDataWrapper.tsx @@ -0,0 +1,20 @@ +import { ReactNode } from 'react' + +type EmptyDataWrapperProps = { + isEmpty: boolean + fallback: ReactNode + children: ReactNode | ReactNode[] +} + +const EmptyDataWrapper: React.FC = ({ + isEmpty, + fallback, + children, +}: EmptyDataWrapperProps) => { + if (isEmpty) { + return <>{fallback} + } + return <>{children} +} + +export default EmptyDataWrapper diff --git a/src/components/domain/empty-data-wrapper/index.tsx b/src/components/domain/empty-data-wrapper/index.tsx new file mode 100644 index 00000000..b2b37d0a --- /dev/null +++ b/src/components/domain/empty-data-wrapper/index.tsx @@ -0,0 +1,3 @@ +import EmptyDataWrapper from './EmptyDataWrapper' + +export default EmptyDataWrapper diff --git a/src/components/domain/exception-boundary/ExceptionBoundary.tsx b/src/components/domain/exception-boundary/ExceptionBoundary.tsx deleted file mode 100644 index 48ee3273..00000000 --- a/src/components/domain/exception-boundary/ExceptionBoundary.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import router from 'next/router' -import Loading from '@/app/loading' -import NoData from '../no-data' - -type ExceptionBoundaryProps = { - isLoading: boolean - isError: boolean - isEmpty: boolean - isFetchingNextPage?: boolean - children: JSX.Element -} - -const ExceptionBoundary: React.FC = ({ - isLoading, - isFetchingNextPage, - isError, - isEmpty, - children, -}: ExceptionBoundaryProps) => { - if (isLoading) { - return
데이터 없음
- } - - if (isError) { - return
에러 발생!
- } - - if (isEmpty) { - return
데이터가 존재하지 않습니다.
- } - return ( - <> - {children} - {isFetchingNextPage && } - - ) -} - -export default ExceptionBoundary diff --git a/src/components/domain/exception-boundary/index.tsx b/src/components/domain/exception-boundary/index.tsx deleted file mode 100644 index 05b9393e..00000000 --- a/src/components/domain/exception-boundary/index.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import ExceptionBoundary from './ExceptionBoundary' - -export default ExceptionBoundary diff --git a/src/components/domain/no-data/NoData.tsx b/src/components/domain/no-data/NoData.tsx index 71acc3be..51611670 100644 --- a/src/components/domain/no-data/NoData.tsx +++ b/src/components/domain/no-data/NoData.tsx @@ -1,15 +1,28 @@ import Image from 'next/image' import Button from '@/components/ui/button' import Assets from '@/config/assets' +import { cn } from '@/utils' type NoDataProps = { title: string onClickButton?: () => void buttonContent: string + position?: boolean } -const NoData = ({ title, onClickButton, buttonContent }: NoDataProps) => { +const NoData = ({ + title, + onClickButton, + buttonContent, + position = true, +}: NoDataProps) => { return ( -
+
no-data
{title}