From 4dc74de5e377b2d70190a96515e360e1c34a7aad Mon Sep 17 00:00:00 2001 From: rabyeoljji Date: Sat, 7 Dec 2024 11:31:10 +0000 Subject: [PATCH] feat: collection detail (#303) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: bookmark collection * fix: 변경된 컬렉션 api 적용 * feat: collection detail --- src/app/(routes)/collections/[id]/page.tsx | 25 ++-- .../collection/components/collection-list.tsx | 15 ++- .../collection/components/collection.tsx | 2 + .../detail-info-with-quizzes/index.tsx | 66 --------- .../components/detail-info/index.tsx | 110 +++++++++++++++ .../collection/components/exploration.tsx | 55 +++----- .../collection/components/my-collection.tsx | 121 ++++++----------- .../components/start-quiz-drawer.tsx | 11 +- .../quiz/components/quiz-card/index.tsx | 2 +- .../quiz/screen/random-quiz-view/index.tsx | 125 +++++++++--------- src/requests/collection/hooks.ts | 9 ++ src/requests/collection/index.ts | 2 +- src/shared/configs/endpoint.ts | 2 +- src/shared/hooks/use-scroll-position.tsx | 36 +++++ src/types/collection.d.ts | 22 ++- src/types/document.d.ts | 6 +- src/types/quiz.d.ts | 26 +--- 17 files changed, 324 insertions(+), 311 deletions(-) delete mode 100644 src/features/collection/components/detail-info-with-quizzes/index.tsx create mode 100644 src/features/collection/components/detail-info/index.tsx create mode 100644 src/shared/hooks/use-scroll-position.tsx diff --git a/src/app/(routes)/collections/[id]/page.tsx b/src/app/(routes)/collections/[id]/page.tsx index b84ca113..80328c58 100644 --- a/src/app/(routes)/collections/[id]/page.tsx +++ b/src/app/(routes)/collections/[id]/page.tsx @@ -1,20 +1,13 @@ -import DetailInfoWithQuizzes from '@/features/collection/components/detail-info-with-quizzes' -import FixedBottom from '@/shared/components/custom/fixed-bottom' -import { Button } from '@/shared/components/ui/button' -import Link from 'next/link' +import DetailInfo from '@/features/collection/components/detail-info' -const CollectionDetailPage = () => { - return ( - <> - - - - {/* 이동 /quiz/[id] - searchParams로 collectionId, createdAt, collectionName, collectionEmoji 넣어서 */} - - - - - ) +interface Props { + params: { + id: string + } +} + +const CollectionDetailPage = ({ params }: Props) => { + return } export default CollectionDetailPage diff --git a/src/features/collection/components/collection-list.tsx b/src/features/collection/components/collection-list.tsx index 1f947db4..4ab7b0e0 100644 --- a/src/features/collection/components/collection-list.tsx +++ b/src/features/collection/components/collection-list.tsx @@ -1,11 +1,18 @@ -import { PropsWithChildren } from 'react' +import { PropsWithChildren, forwardRef } from 'react' -const CollectionList = ({ children }: PropsWithChildren) => { +interface CollectionListProps extends PropsWithChildren {} + +const CollectionList = forwardRef(({ children }, ref) => { return ( -
+
{children}
) -} +}) + +CollectionList.displayName = 'CollectionList' export default CollectionList diff --git a/src/features/collection/components/collection.tsx b/src/features/collection/components/collection.tsx index 720edd0b..8ad3f167 100644 --- a/src/features/collection/components/collection.tsx +++ b/src/features/collection/components/collection.tsx @@ -40,6 +40,7 @@ const Collection = ({ name="book-mark-fill" className="size-[24px] text-icon-disabled" onClick={(e) => { + e.preventDefault() e.stopPropagation() }} /> @@ -50,6 +51,7 @@ const Collection = ({ name="book-mark-fill" className="size-[24px] cursor-pointer" onClick={(e) => { + e.preventDefault() e.stopPropagation() bookmarkMutate({ collectionId, isBookMarked: true }) }} diff --git a/src/features/collection/components/detail-info-with-quizzes/index.tsx b/src/features/collection/components/detail-info-with-quizzes/index.tsx deleted file mode 100644 index 471ac87c..00000000 --- a/src/features/collection/components/detail-info-with-quizzes/index.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import QuizCard from '@/features/quiz/components/quiz-card' -import { quizzes } from '@/features/quiz/config' -import CategoryTag from '@/shared/components/custom/category-tag' -import Text from '@/shared/components/ui/text' - -const DetailInfoWithQuizzes = () => { - return ( - <> -
-
-
- 🌼 -
- - 파이썬기본문법과응용 - - -
-
-
-
- - 35 문제 - -
- - 객관식 30 - -
- - O/X - -
-
- - 이 퀴즈는 제가 파이썬을 공부하며 생성한 퀴즈 중 자주 틀린 퀴즈만 모은 컬렉션입니다 - 공부에 도움이 되시길 바라며... - -
-
- -
-
- {quizzes.map((quiz) => ( - - - Q. - - - {/* 내가 만든 컬렉션일 경우 디렉토리 > 문서이름 이런식으로 breadcrumb 필요 */} - {/* 전공 공부 {'>'} 최근이슈 */} -
- } - key={quiz.id} - quiz={quiz} - /> - ))} -
-
- - ) -} - -export default DetailInfoWithQuizzes diff --git a/src/features/collection/components/detail-info/index.tsx b/src/features/collection/components/detail-info/index.tsx new file mode 100644 index 00000000..be5492f1 --- /dev/null +++ b/src/features/collection/components/detail-info/index.tsx @@ -0,0 +1,110 @@ +'use client' + +import QuizCard from '@/features/quiz/components/quiz-card' +import { useCollectionInfo } from '@/requests/collection/hooks' +import CategoryTag from '@/shared/components/custom/category-tag' +import FixedBottom from '@/shared/components/custom/fixed-bottom' +import Loading from '@/shared/components/custom/loading' +import { Button } from '@/shared/components/ui/button' +import Text from '@/shared/components/ui/text' +import { useUser } from '@/shared/hooks/use-user' +import Link from 'next/link' +import { useMemo } from 'react' + +interface Props { + id: number +} + +const DetailInfo = ({ id }: Props) => { + const { user } = useUser() + const { data: collectionData } = useCollectionInfo(id) + + const quizCounts = useMemo(() => { + if (!collectionData?.quizzes) return { multiple: 0, ox: 0 } + + return collectionData.quizzes.reduce( + (acc, quiz) => { + if (quiz.quizType === 'MULTIPLE_CHOICE') { + acc.multiple += 1 + } else if (quiz.quizType === 'MIX_UP') { + acc.ox += 1 + } + return acc + }, + { multiple: 0, ox: 0 } + ) + }, [collectionData?.quizzes]) + + const isMine = user?.id === collectionData?.member.creatorId + + /** TODO: Spinner로 대체 */ + if (!collectionData) return + + return ( + <> +
+
+
+ {collectionData.emoji} +
+ + {collectionData.name} + + +
+
+
+
+ + {quizCounts.multiple + quizCounts.ox} 문제 + +
+ + 객관식 {quizCounts.multiple} + +
+ + O/X {quizCounts.ox} + +
+
+ + {collectionData.description} + +
+
+ + {isMine && ( +
+
+ {collectionData.quizzes.map((quiz) => ( + + + Q. + + + {/* 내가 만든 컬렉션일 경우 디렉토리 > 문서이름 이런식으로 breadcrumb 필요 */} + {/* 전공 공부 {'>'} 최근이슈 */} +
+ } + key={quiz.id} + quiz={quiz} + /> + ))} +
+
+ )} + + + + {/* 이동 /quiz/[id] - searchParams로 collectionId, createdAt, collectionName, collectionEmoji 넣어서 */} + + + + + ) +} + +export default DetailInfo diff --git a/src/features/collection/components/exploration.tsx b/src/features/collection/components/exploration.tsx index 4de98875..5ddeb94a 100644 --- a/src/features/collection/components/exploration.tsx +++ b/src/features/collection/components/exploration.tsx @@ -4,18 +4,20 @@ import Icon from '@/shared/components/custom/icon' import Collection from './collection' import CollectionList from './collection-list' import Text from '@/shared/components/ui/text' -import StartQuizDrawer from './start-quiz-drawer' -import { useBookmarkedCollections, useCollections } from '@/requests/collection/hooks' +import { useCollections } from '@/requests/collection/hooks' import Loading from '@/shared/components/custom/loading' import { useUser } from '@/shared/hooks/use-user' +import Link from 'next/link' +import { useScrollPosition } from '@/shared/hooks/use-scroll-position' const controlButtons = ['분야', '퀴즈 유형', '문제 수'] const Exploration = () => { const { data: collectionsData, isLoading } = useCollections() - const { data: bookmarkedCollections, isLoading: isBookmarkedLoading } = useBookmarkedCollections() const { user } = useUser() + const scrollContainerRef = useScrollPosition({ pageKey: 'exploration' }) + return ( <>
@@ -35,50 +37,25 @@ const Exploration = () => {
- - {isBookmarkedLoading || isLoading ? ( + + {isLoading ? ( ) : ( - collectionsData?.collections.map((collection) => { - const isBookmarked = Boolean( - bookmarkedCollections?.collections.some( - (bookmarkedCollection) => bookmarkedCollection.id === collection.id - ) - ) - const multipleChoiceCount = - collection.quizzes?.filter((quiz) => quiz.quizType === 'MULTIPLE_CHOICE').length ?? 0 - const oxCount = - collection.quizzes?.filter((quiz) => quiz.quizType === 'MIX_UP').length ?? 0 - - return ( - ( + + - } /> - ) - }) + + )) )} diff --git a/src/features/collection/components/my-collection.tsx b/src/features/collection/components/my-collection.tsx index 8d24e61e..4867fd53 100644 --- a/src/features/collection/components/my-collection.tsx +++ b/src/features/collection/components/my-collection.tsx @@ -2,42 +2,51 @@ import Text from '@/shared/components/ui/text' import { cn } from '@/shared/lib/utils' -import { useState } from 'react' import Collection from './collection' import CollectionList from './collection-list' import Link from 'next/link' import Icon from '@/shared/components/custom/icon' -import StartQuizDrawer from './start-quiz-drawer' import { useBookmarkedCollections, useMyCollections } from '@/requests/collection/hooks' import Loading from '@/shared/components/custom/loading' import { SwitchCase } from '@/shared/components/custom/react/switch-case' +import { useRouter, useSearchParams } from 'next/navigation' +import { useScrollPosition } from '@/shared/hooks/use-scroll-position' -const tabs = [ +const sort = [ { key: 'create-collection', label: '만든 컬렉션' }, { key: 'save-collection', label: '보관한 컬렉션' }, ] as const +type TabType = (typeof sort)[number]['key'] + const MyCollection = () => { - const [activeTab, setActiveTab] = useState<'create-collection' | 'save-collection'>( - 'create-collection' - ) + const router = useRouter() + const searchParams = useSearchParams() + const activeTab = (searchParams.get('sort') as TabType) || 'create-collection' + + const scrollContainerRef = useScrollPosition({ pageKey: 'my-collection' }) const { data: myCollectionsData, isLoading: isMyCollectionLoading } = useMyCollections() const { data: bookmarkedCollectionsData, isLoading: isBookmarkedCollectionLoading } = useBookmarkedCollections() - const { data: bookmarkedCollections, isLoading: isBookmarkedLoading } = useBookmarkedCollections() + + const handleTabChange = (tab: TabType) => { + const params = new URLSearchParams(searchParams) + params.set('sort', tab) + router.replace(`?${params.toString()}`) + } return ( <>
- {tabs.map((tab) => ( + {sort.map((tab) => (