diff --git a/next.config.js b/next.config.js index 5bbcb6c8..43d4b393 100644 --- a/next.config.js +++ b/next.config.js @@ -34,6 +34,18 @@ const nextConfig = { port: '', pathname: '/**', }, + { + protocol: 'http', + hostname: 'k.kakaocdn.net', + port: '', + pathname: '/**', + }, + { + protocol: 'https', + hostname: 'blog.kakaocdn.net', + port: '', + pathname: '/**', + }, ], }, }; diff --git a/public/icons/post.svg b/public/icons/post.svg index ff08f58e..dac5f263 100644 --- a/public/icons/post.svg +++ b/public/icons/post.svg @@ -1,3 +1,3 @@ - - + + diff --git a/src/apis/user.tsx b/src/apis/user.tsx index b1f1b3b3..c4910924 100644 --- a/src/apis/user.tsx +++ b/src/apis/user.tsx @@ -1,10 +1,10 @@ import { APIJob, APIJobGroup } from '@/types/job'; -import { APIUser } from '@/types/user'; +import { APIUser, APIUserProfile } from '@/types/user'; import { publicApi } from './core/axios'; const userAPI = { getUserProfile: ({ userId }: { userId: APIUser['userId'] }) => - publicApi.get(`/service-api/users/${userId}/profile`), + publicApi.get(`/service-api/users/${userId}/profile`), getMyProfile: () => publicApi.get('/service-api/users/me'), diff --git a/src/app/group/[groupId]/page.tsx b/src/app/group/[groupId]/page.tsx index bf01dac1..8cc21aea 100644 --- a/src/app/group/[groupId]/page.tsx +++ b/src/app/group/[groupId]/page.tsx @@ -1,19 +1,52 @@ 'use client'; -import { Flex } from '@chakra-ui/react'; +import TopNavigation from '@/ui/Base/TopNavigation'; +import BookGroupInfo from '@/v1/bookGroup/detail/BookGroupInfo'; +import { IconArrowLeft, IconHamburger, IconPost } from '@public/icons'; -import GroupDetail from '@/ui/Group/GroupDetail'; +import { useBookGroupTitle } from '@/queries/group/useBookGroupQuery'; +import CommentList from '@/v1/bookGroup/detail/CommentList'; -const GroupDetailPage = ({ +const DetailBookGroupPage = ({ params: { groupId }, }: { params: { groupId: number }; }) => { return ( - - - + <> + +
+ +
+ + +
+
+ ); }; -export default GroupDetailPage; +export default DetailBookGroupPage; + +const BookGroupNavigation = ({ groupId }: { groupId: number }) => { + const { data: title } = useBookGroupTitle(groupId); + + return ( + + + + + + {title} + + + + + + + ); +}; + +const Heading = ({ text }: { text: string }) => ( +

{text}

+); diff --git a/src/hooks/useQueryWithSuspense.ts b/src/hooks/useQueryWithSuspense.ts index 349be76b..835f2b1a 100644 --- a/src/hooks/useQueryWithSuspense.ts +++ b/src/hooks/useQueryWithSuspense.ts @@ -7,7 +7,7 @@ import { UseQueryResult, } from '@tanstack/react-query'; -export type useQueryOptionWithOutSuspense< +export type UseQueryOptionWithoutSuspense< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, @@ -22,7 +22,7 @@ const useQueryWithSuspense = < >( queryKey: TQueryKey, queryFunction?: QueryFunction, - queryOptions?: useQueryOptionWithOutSuspense< + queryOptions?: UseQueryOptionWithoutSuspense< TQueryFnData, TError, TData, diff --git a/src/queries/group/key.ts b/src/queries/group/key.ts new file mode 100644 index 00000000..1fba2efb --- /dev/null +++ b/src/queries/group/key.ts @@ -0,0 +1,12 @@ +import { APIGroupDetail } from '@/types/group'; + +const bookGroupKeys = { + all: ['bookGroup'] as const, + details: () => [...bookGroupKeys.all, 'detail'] as const, + detail: (id: APIGroupDetail['bookGroupId']) => + [...bookGroupKeys.details(), id] as const, + comments: (id: APIGroupDetail['bookGroupId']) => + [...bookGroupKeys.details(), id, 'comments'] as const, +}; + +export default bookGroupKeys; diff --git a/src/queries/group/useBookGroupCommentsQuery.ts b/src/queries/group/useBookGroupCommentsQuery.ts new file mode 100644 index 00000000..aff049cd --- /dev/null +++ b/src/queries/group/useBookGroupCommentsQuery.ts @@ -0,0 +1,40 @@ +import { useQuery } from '@tanstack/react-query'; +import { QueryOptions } from '@/types/query'; +import { + APIGroupCommentPagination, + APIGroupDetail, + BookGroupComment, +} from '@/types/group'; + +import GroupAPI from '@/apis/group'; +import bookGroupKeys from './key'; + +const transformComments = ({ bookGroupComments }: APIGroupCommentPagination) => + bookGroupComments.map(comment => ({ + id: comment.commentId, + writer: { + id: comment.userId, + profileImageSrc: comment.userProfileImage, + name: comment.nickname, + }, + createdAt: comment.createdAt, + content: comment.contents, + })); + +const useBookGroupCommentsQuery = ( + groupId: APIGroupDetail['bookGroupId'], + select?: QueryOptions['select'] +) => + useQuery({ + queryKey: bookGroupKeys.comments(groupId), + queryFn: () => + GroupAPI.getGroupComments({ bookGroupId: groupId }).then( + ({ data }) => data + ), + select, + }); + +export default useBookGroupCommentsQuery; + +export const useBookGroupComments = (groupId: APIGroupDetail['bookGroupId']) => + useBookGroupCommentsQuery(groupId, transformComments); diff --git a/src/queries/group/useBookGroupQuery.ts b/src/queries/group/useBookGroupQuery.ts new file mode 100644 index 00000000..cd5b2982 --- /dev/null +++ b/src/queries/group/useBookGroupQuery.ts @@ -0,0 +1,40 @@ +import { useQuery } from '@tanstack/react-query'; + +import { APIGroupDetail, BookGroupDetail } from '@/types/group'; +import { QueryOptions } from '@/types/query'; + +import GroupAPI from '@/apis/group'; +import bookGroupKeys from './key'; + +const transformBookGroupDetail = (data: APIGroupDetail) => + ({ + title: data.title, + description: data.introduce, + bookId: data.book.id, + owner: { isMe: data.isOwner, id: data.owner.id }, + date: { start: data.startDate, end: data.endDate }, + memberCount: { current: data.currentMemberCount, max: data.maxMemberCount }, + isPublic: data.isPublic, + isMember: data.isGroupMember, + } as BookGroupDetail); + +export const useBookGroupQuery = ( + groupId: APIGroupDetail['bookGroupId'], + select: QueryOptions['select'] +) => + useQuery({ + queryKey: bookGroupKeys.detail(groupId), + queryFn: () => + GroupAPI.getGroupDetailInfo({ bookGroupId: groupId }).then( + ({ data }) => data + ), + select, + }); + +export default useBookGroupQuery; + +export const useBookGroup = (groupId: APIGroupDetail['bookGroupId']) => + useBookGroupQuery(groupId, transformBookGroupDetail); + +export const useBookGroupTitle = (groupId: APIGroupDetail['bookGroupId']) => + useBookGroupQuery(groupId, data => data.title); diff --git a/src/queries/group/useGroupCommentsQuery/index.tsx b/src/queries/group/useGroupCommentsQuery/index.tsx deleted file mode 100644 index 7cc0c1ad..00000000 --- a/src/queries/group/useGroupCommentsQuery/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import GroupAPI from '@/apis/group'; -import { APIGroupDetail } from '@/types/group'; -import { useQuery } from '@tanstack/react-query'; - -const useGroupCommentsQuery = ({ - bookGroupId, -}: { - bookGroupId: APIGroupDetail['bookGroupId']; -}) => - useQuery(['group', bookGroupId, 'comments'], () => - GroupAPI.getGroupComments({ bookGroupId }).then(({ data }) => data) - ); - -export default useGroupCommentsQuery; diff --git a/src/queries/user/key.ts b/src/queries/user/key.ts new file mode 100644 index 00000000..b5434f72 --- /dev/null +++ b/src/queries/user/key.ts @@ -0,0 +1,10 @@ +import { APIUser } from '@/types/user'; + +const userKeys = { + all: ['user'] as const, + details: () => [...userKeys.all, 'detail'] as const, + detail: (id: APIUser['userId']) => [...userKeys.details(), id] as const, + me: () => [...userKeys.details(), 'me'] as const, +}; + +export default userKeys; diff --git a/src/queries/user/useMyProfileQuery.ts b/src/queries/user/useMyProfileQuery.ts index f9356487..b433d463 100644 --- a/src/queries/user/useMyProfileQuery.ts +++ b/src/queries/user/useMyProfileQuery.ts @@ -1,12 +1,13 @@ import userAPI from '@/apis/user'; import type { APIUser } from '@/types/user'; import useQueryWithSuspense, { - useQueryOptionWithOutSuspense, + UseQueryOptionWithoutSuspense, } from '@/hooks/useQueryWithSuspense'; +import userKeys from './key'; -const useMyProfileQuery = (options?: useQueryOptionWithOutSuspense) => +const useMyProfileQuery = (options?: UseQueryOptionWithoutSuspense) => useQueryWithSuspense( - ['user', 'me'], + userKeys.me(), () => userAPI.getMyProfile().then(({ data }) => data), options ); diff --git a/src/queries/user/useUserProfileQuery.ts b/src/queries/user/useUserProfileQuery.ts index f064c294..ac2c183d 100644 --- a/src/queries/user/useUserProfileQuery.ts +++ b/src/queries/user/useUserProfileQuery.ts @@ -1,15 +1,17 @@ -import userAPI from '@/apis/user'; +import type { APIUser, APIUserProfile } from '@/types/user'; import useQueryWithSuspense, { - useQueryOptionWithOutSuspense, + UseQueryOptionWithoutSuspense, } from '@/hooks/useQueryWithSuspense'; -import type { APIUser } from '@/types/user'; + +import userAPI from '@/apis/user'; +import userKeys from './key'; const useUserProfileQuery = ( userId: APIUser['userId'], - options?: useQueryOptionWithOutSuspense + options?: UseQueryOptionWithoutSuspense ) => useQueryWithSuspense( - ['user', String(userId)], + userKeys.detail(userId), () => userAPI.getUserProfile({ userId }).then(({ data }) => data), options ); diff --git a/src/stories/bookGroup/detail/BookGroupInfo.stories.tsx b/src/stories/bookGroup/detail/BookGroupInfo.stories.tsx deleted file mode 100644 index d9b4daa4..00000000 --- a/src/stories/bookGroup/detail/BookGroupInfo.stories.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; - -import BookGroupInfo from '@/v1/bookGroup/detail/BookGroupInfo'; - -const meta: Meta = { - title: 'bookgroup/detail/BookGroupInfo', - component: BookGroupInfo, - tags: ['autodocs'], -}; - -export default meta; - -type Story = StoryObj; - -export const Default: Story = { - args: { - title: '프롱이 리팩터링 스터디', - description: - '제 1차 프롱이 기수연합 독서 스터디 입니다. 마틴 파울러의 저서 ‘리팩터링 2판’과 함께 진행합니다.', - book: { - title: '리팩터링 2판', - author: '마틴 파울러', - bookImageSrc: 'https://image.yes24.com/goods/89649360/XL', - }, - date: { - start: '2023-10-31', - end: '2023-11-27', - }, - memberCount: { - current: 3, - max: 20, - }, - owner: { - isMe: true, - name: '소피아', - profileImageSrc: '/icons/logo.svg', - }, - isPublic: false, - }, -}; diff --git a/src/stories/bookGroup/detail/CommentList.stories.tsx b/src/stories/bookGroup/detail/CommentList.stories.tsx deleted file mode 100644 index f15343f4..00000000 --- a/src/stories/bookGroup/detail/CommentList.stories.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; - -import CommentList from '@/v1/bookGroup/detail/CommentList'; - -const meta: Meta = { - title: 'bookgroup/detail/CommentList', - component: CommentList, - tags: ['autodocs'], -}; - -export default meta; - -type Story = StoryObj; - -export const Default: Story = { - args: { - comments: [ - { - id: 1, - writer: { id: 10, name: '소피아', profileImageSrc: '/icons/logo.svg' }, - createdAt: '2023.10.22', - content: '프론트엔드 개발자라면 꼭 읽어봐야 할 책이라고 생각해요.', - }, - { - id: 2, - writer: { id: 21, name: '다독이', profileImageSrc: '' }, - createdAt: '2023.10.18', - content: '이 책 덕분에 프로젝트 리팩터링에 도전할 수 있었어요.', - }, - ], - }, -}; diff --git a/src/types/group.ts b/src/types/group.ts index 4aed9396..0e8638f5 100644 --- a/src/types/group.ts +++ b/src/types/group.ts @@ -23,7 +23,7 @@ export interface APIGroup { hasJoinPasswd: boolean; isPublic: boolean; bookGroupId: number; - memberCount: number; + currentMemberCount: number; commentCount: number; book: APIGroupBook; owner: APIGroupOwner; @@ -65,10 +65,36 @@ export interface APIGroupComment { userProfileImage: APIUser['profileImage']; createdAt: string; modifiedAt: string; - nickname: APIUser['nickname']; + nickname: string; writtenByCurrentUser: boolean; } export interface APIGroupCommentPagination extends Pagination { + bookGroup: { isPublic: APIGroup['isPublic'] }; bookGroupComments: APIGroupComment[]; } + +export type BookGroupDetail = { + title: APIGroup['title']; + description: APIGroup['introduce']; + bookId: APIBook['bookId']; + owner: { isMe: boolean; id: APIUser['userId'] }; + date: { start: APIGroup['startDate']; end: APIGroup['endDate'] }; + memberCount: { + current: APIGroup['currentMemberCount']; + max: APIGroup['maxMemberCount']; + }; + isPublic: APIGroup['isPublic']; + isMember: APIGroupDetail['isGroupMember']; +}; + +export type BookGroupComment = { + id: APIGroup['bookGroupId']; + writer: { + id: APIUser['userId']; + profileImageSrc: APIUser['profileImage']; + name: APIUser['nickname']; + }; + createdAt: APIGroupComment['createdAt']; + content: APIGroupComment['contents']; +}; diff --git a/src/types/query.ts b/src/types/query.ts index 75ca7662..d37e0218 100644 --- a/src/types/query.ts +++ b/src/types/query.ts @@ -1,3 +1,6 @@ import { UseQueryOptions } from '@tanstack/react-query'; -export type QueryOptions = UseQueryOptions, unknown, T, string[]>; +export type QueryOptions< + TQueryFnData, + TQueryData = TQueryFnData +> = UseQueryOptions, unknown, TQueryData, string[]>; diff --git a/src/types/user.ts b/src/types/user.ts index 1bbdf884..1c441a71 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -1,13 +1,20 @@ import { APIProfileJob } from './job'; -export interface APIUser { +export interface APIUserProfile { userId: number; + nickname: string; + profileImage: string; + gender: string | null; + job: APIProfileJob; +} + +export interface APIMyProfile extends Omit { name: string | null; nickname: string | null; oauthNickname: string; email: string | null; - profileImage: string; - gender: true; - authProvider: true; - job: APIProfileJob; + gender: string; + authProvider: string; } + +export type APIUser = APIUserProfile & { name: string | null }; diff --git a/src/ui/Base/TopNavigation.tsx b/src/ui/Base/TopNavigation.tsx index 380b2661..809df908 100644 --- a/src/ui/Base/TopNavigation.tsx +++ b/src/ui/Base/TopNavigation.tsx @@ -32,9 +32,7 @@ const textAligns = { const CenterItem = ({ children, textAlign = 'center' }: CenterItemProps) => { const alignClassName = textAligns[textAlign]; return ( -
- {children} -
+
{children}
); }; diff --git a/src/ui/Group/GroupDetail/index.tsx b/src/ui/Group/GroupDetail/index.tsx index 7a609b11..b9f8756e 100644 --- a/src/ui/Group/GroupDetail/index.tsx +++ b/src/ui/Group/GroupDetail/index.tsx @@ -5,7 +5,7 @@ import GroupInfo from '@/ui/Group/GroupDetail/GroupInfo'; import CommentInputBox from '../GroupComment/CommentInputBox'; import CommentsList from '../GroupComment'; import useGroupInfoQuery from '@/queries/group/useGroupInfoQuery'; -import useGroupCommentsQuery from '@/queries/group/useGroupCommentsQuery'; +import useGroupCommentsQuery from '@/queries/group/useBookGroupCommentsQuery'; import GroupAPI from '@/apis/group'; import { useToast } from '@/hooks/toast'; @@ -15,7 +15,7 @@ interface GroupDetailProps { const GroupDetail = ({ bookGroupId }: GroupDetailProps) => { const groupInfoQuery = useGroupInfoQuery({ bookGroupId }); - const groupCommentsQuery = useGroupCommentsQuery({ bookGroupId }); + const groupCommentsQuery = useGroupCommentsQuery(bookGroupId); const { showToast } = useToast(); const router = useRouter(); diff --git a/src/ui/Group/GroupList/GroupListItem.tsx b/src/ui/Group/GroupList/GroupListItem.tsx index 5c929d8f..3df7ecca 100644 --- a/src/ui/Group/GroupList/GroupListItem.tsx +++ b/src/ui/Group/GroupList/GroupListItem.tsx @@ -12,7 +12,7 @@ const GroupListItem = ({ endDate, maxMemberCount, owner, - memberCount, + currentMemberCount, commentCount, book, }: APIGroup) => { @@ -99,7 +99,7 @@ const GroupListItem = ({ peopleIcon - {memberCount} + {currentMemberCount} {maxMemberCount ? ` / ${maxMemberCount}` : ''} diff --git a/src/ui/Profile/ProfileInfo/ProfileInfoPresenter.tsx b/src/ui/Profile/ProfileInfo/ProfileInfoPresenter.tsx index 6eca059a..14ee3c31 100644 --- a/src/ui/Profile/ProfileInfo/ProfileInfoPresenter.tsx +++ b/src/ui/Profile/ProfileInfo/ProfileInfoPresenter.tsx @@ -2,16 +2,12 @@ import { Avatar, Flex, HStack, Text, VStack } from '@chakra-ui/react'; import type { APIUser } from '@/types/user'; import IconButton from '../../common/IconButton'; -type ProfileInfoProps = Pick< - APIUser, - 'nickname' | 'oauthNickname' | 'profileImage' | 'email' | 'job' ->; +type ProfileInfoProps = Pick; +// COMMENT: 프로필 정보 조회 API 스키마 변경으로 email, oauthnickname props 제거 const ProfileInfoPresenter = ({ nickname, - oauthNickname, profileImage, - email, job: { jobGroupKoreanName, jobNameKoreanName }, }: ProfileInfoProps) => { return ( @@ -19,8 +15,8 @@ const ProfileInfoPresenter = ({ - {nickname || oauthNickname} - {email} + {nickname} + {/* {email} */} diff --git a/src/v1/book/BookCover.tsx b/src/v1/book/BookCover.tsx index b3188cad..c6dd3017 100644 --- a/src/v1/book/BookCover.tsx +++ b/src/v1/book/BookCover.tsx @@ -45,7 +45,7 @@ const BookCover = ({ src, title, size = 'medium' }: BookCoverProps) => { const sizeClasses = getCoverSizeClasses(size); return ( -
+
{title} { + const { data: bookGroupInfo } = useBookGroup(groupId); -const BookGroupInfo = ({ - title, - description, - book, - date, - memberCount, - owner, - isPublic, -}: BookGroupInfoProps) => { return ( -
-
- - -
- - - <BookInfoCard - title={book.title} - bookImageSrc={book.bookImageSrc} - author={book.author} - /> - <div className="flex flex-col gap-[0.3rem]"> - <Duration start={date.start} end={date.end} /> - <MemberCapacity current={memberCount.current} max={memberCount.max} /> - </div> - <Description content={description} /> + <div className="flex flex-col gap-[1rem] py-[2rem]"> + {bookGroupInfo && ( + <> + <div className="flex gap-[0.5rem]"> + <BookGroupStatus + start={bookGroupInfo.date.start} + end={bookGroupInfo.date.end} + /> + <Public isPublic={bookGroupInfo.isPublic} /> + </div> + <Owner + userId={bookGroupInfo.owner.id} + isMe={bookGroupInfo.owner.isMe} + /> + <Title title={bookGroupInfo.title} /> + <BookInfoCard bookId={bookGroupInfo.bookId} /> + <div className="flex flex-col gap-[0.3rem]"> + <Duration + start={bookGroupInfo.date.start} + end={bookGroupInfo.date.end} + /> + <MemberCapacity + current={bookGroupInfo.memberCount.current} + max={bookGroupInfo.memberCount.max} + /> + </div> + <Description content={bookGroupInfo.description} /> + </> + )} </div> ); }; @@ -58,29 +52,28 @@ const Public = ({ isPublic }: { isPublic: boolean }) => ( ); const Owner = ({ - profileImageSrc, - name, - isMe, + userId, + isMe = false, }: { - profileImageSrc: string; - name: string; - isMe: boolean; + userId: number; + isMe?: boolean; }) => { + const { data: userInfo } = useUserProfileQuery(userId); + return ( <div className="flex items-center gap-[1rem]"> - {/** FIXME: Avatar 컴포넌트로 변경 */} - <Image - width={32} - height={32} - alt={name} - src={profileImageSrc} - className="rounded-full" - placeholder="blur" - blurDataURL={DATA_URL['placeholder']} - /> - <span className="text-center text-sm font-bold"> - {name} {isMe && ' 👑'} - </span> + {userInfo && ( + <> + <Avatar + name={userInfo.nickname} + size="medium" + src={userInfo.profileImage} + /> + <span className="text-center text-sm font-bold"> + {userInfo.nickname} {isMe && ' 👑'} + </span> + </> + )} </div> ); }; @@ -89,24 +82,26 @@ const Title = ({ title }: { title: string }) => { return <p className="text-xl font-bold">{title}</p>; }; -const BookInfoCard = ({ - bookImageSrc, - title, - author, -}: { - bookImageSrc: string; - title: string; - author: string; -}) => { +const BookInfoCard = ({ bookId }: { bookId: number }) => { + const { data: bookInfo } = useBookInfoQuery(bookId); + return ( <div className="flex min-h-[10rem] w-full cursor-pointer gap-[2.4rem] rounded-[0.5rem] border-[0.05rem] border-cancel px-[2.2rem] py-[1.8rem]"> - <BookCover size="xsmall" src={bookImageSrc} title={title} /> - <div className="flex flex-grow flex-col"> - <span className="text-sm font-bold">{title}</span> - <span className="text-xs text-placeholder">{author}</span> - </div> - {/** 왼쪽 방향의 화살표를 180도 회전하여 사용 */} - <IconArrowLeft className="h-[1.5rem] w-[1.5rem] rotate-180" /> + {bookInfo && ( + <> + <BookCover + size="xsmall" + src={bookInfo.imageUrl} + title={bookInfo.title} + /> + <div className="flex min-w-0 flex-grow flex-col"> + <span className="truncate text-sm font-bold">{bookInfo.title}</span> + <span className="text-xs text-placeholder">{bookInfo.author}</span> + </div> + {/** 왼쪽 방향의 화살표를 180도 회전하여 사용 */} + <IconArrowLeft className="h-[1.5rem] w-[1.5rem] flex-shrink-0 rotate-180" /> + </> + )} </div> ); }; @@ -116,20 +111,26 @@ const Duration = ({ start, end }: { start: string; end: string }) => { <div className="flex items-center gap-[1rem]"> <IconCalendar className="h-auto w-[1.6rem] fill-placeholder" /> <span className="text-sm text-placeholder"> - {start} - {end} + {start} ~ {end} </span> </div> ); }; -const MemberCapacity = ({ current, max }: { current: number; max: number }) => { +const MemberCapacity = ({ + current, + max, +}: { + current: number; + max: number | null; +}) => { return ( <div className="flex items-center gap-[1rem]"> <IconMembers className="h-auto w-[1.6rem] fill-placeholder" /> - <span className="text-sm text-placeholder"> + <p className="text-sm text-placeholder"> <span className="text-main-900">{current}</span> - {` / ${max}명`} - </span> + {`${max ? ` / ${max}` : ''}명`} + </p> </div> ); }; diff --git a/src/v1/bookGroup/detail/CommentList.tsx b/src/v1/bookGroup/detail/CommentList.tsx index bedededb..fd23505a 100644 --- a/src/v1/bookGroup/detail/CommentList.tsx +++ b/src/v1/bookGroup/detail/CommentList.tsx @@ -1,70 +1,37 @@ -import Image from 'next/image'; - import { IconHamburger } from '@public/icons'; +import Avatar from '@/ui/Base/Avatar'; +import { useBookGroupComments } from '@/queries/group/useBookGroupCommentsQuery'; -type Comment = { - id: number; - writer: { id: number; profileImageSrc: string; name: string }; - createdAt: string; - content: string; -}; - -interface CommentListProps { - comments: Comment[]; -} +const CommentList = ({ groupId }: { groupId: number }) => { + const { data: comments } = useBookGroupComments(groupId); -const CommentList = ({ comments }: CommentListProps) => { return ( <div className="flex flex-col gap-[1rem]"> - {/* FIXME: Heading 컴포넌트 페이지 단으로 빼기 */} - <Heading text="게시글" /> - - {comments.map(({ id, writer, createdAt, content }) => ( - <div className="flex flex-col gap-[2rem] pt-[1rem]" key={id}> - <div className="flex gap-[1rem]"> - <Avatar - profileImageSrc={writer.profileImageSrc} - name={writer.name} - /> - <div className="flex flex-grow flex-col"> - <Name name={writer.name} /> - <Date date={createdAt} /> + {comments && + comments.map(({ id, writer, createdAt, content }) => ( + <div className="flex flex-col gap-[1.7rem] pt-[1rem]" key={id}> + <div className="flex gap-[1rem]"> + <Avatar + src={writer.profileImageSrc} + name={writer.name} + size="medium" + /> + <div className="flex flex-grow flex-col"> + <Name name={writer.name} /> + <Date date={createdAt} /> + </div> + <MenuButton /> </div> - <MenuButton /> + <Comment content={content} /> + <Divider /> </div> - <Comment content={content} /> - <Divider /> - </div> - ))} + ))} </div> ); }; export default CommentList; -const Heading = ({ text }: { text: string }) => ( - <p className="text-xl font-bold">{text}</p> -); - -// FIXME: Avatar Base 컴포넌트로 변경 -const Avatar = ({ - profileImageSrc, - name, -}: { - profileImageSrc: string; - name: string; -}) => ( - <span - className={`relative h-[3.5rem] w-[3.5rem] self-center rounded-full ${ - profileImageSrc ? 'bg-white' : 'bg-black-400' - }`} - > - {profileImageSrc && ( - <Image alt={name} src={profileImageSrc} fill className="rounded-full" /> - )} - </span> -); - const Name = ({ name }: { name: string }) => ( <p className="text-sm font-bold">{name}</p> );