-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[#439] [독서모임] 모임 목록 페이지 리팩토링 #440
Changes from 3 commits
2f925c6
a5465bc
1b8b293
ead74ea
c9c0cc1
2798e04
0dd536c
6517b39
12e7e94
bf719dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1,38 @@ | ||
'use client'; | ||
|
||
import TopHeader from '@/ui/Base/TopHeader'; | ||
import SearchGroup from '@/v1/bookGroup/SearchGroup'; | ||
import SimpleBookGroupCard from '@/v1/bookGroup/SimpleBookGroupCard'; | ||
import DetailBookGroupCard from '@/v1/bookGroup/DetailBookGroupCard'; | ||
|
||
import useEntireGroupsQuery from '@/queries/group/useEntireGroupsQuery'; | ||
import GroupHeader from '@/ui/Group/GroupHeader'; | ||
import GroupList from '@/ui/Group/GroupList'; | ||
import GroupSearch from '@/ui/Group/GroupSearch'; | ||
import { Box, Skeleton, VStack } from '@chakra-ui/react'; | ||
import { useState, useEffect } from 'react'; | ||
import useMyGroupsQuery from '@/queries/group/useMyGroupsQuery'; | ||
import Link from 'next/link'; | ||
import { Skeleton, VStack } from '@chakra-ui/react'; | ||
import { useEffect } from 'react'; | ||
import { useInView } from 'react-intersection-observer'; | ||
|
||
interface SearchValue { | ||
[key: string]: string; | ||
input: string; | ||
select: string; | ||
} | ||
|
||
const GroupPage = () => { | ||
const [searchValue, setSearchValue] = useState<SearchValue>({ | ||
input: '', | ||
select: '모임', | ||
}); | ||
const { ref, inView } = useInView(); | ||
|
||
const { | ||
isSuccess, | ||
data, | ||
isSuccess: entireGroupsIsSuccess, | ||
data: entireGroupsData, | ||
isLoading, | ||
fetchNextPage, | ||
hasNextPage, | ||
isFetchingNextPage, | ||
} = useEntireGroupsQuery(); | ||
|
||
const { isSuccess: myGroupsIsSuccess, data: myGroupsData } = | ||
useMyGroupsQuery(); | ||
|
||
useEffect(() => { | ||
if (inView && hasNextPage) { | ||
fetchNextPage(); | ||
} | ||
}, [fetchNextPage, inView, hasNextPage]); | ||
|
||
const handleSumbit = () => { | ||
const { input } = searchValue; | ||
if (input.trim().length === 0) { | ||
/*공백만 입력한 경우 전체 데이터 렌더링 */ | ||
} else { | ||
/*검색 API호출 및 setMeetingListData 업데이트 */ | ||
} | ||
}; | ||
|
||
const handleChange = (name: string, value: string) => { | ||
if (!(name in searchValue)) return; | ||
const tempSearchValue = { ...searchValue }; | ||
tempSearchValue[name] = value; | ||
setSearchValue(tempSearchValue); | ||
}; | ||
|
||
if (isLoading) | ||
return ( | ||
<VStack gap="0.5rem" align="stretch" w="100%"> | ||
|
@@ -63,22 +44,77 @@ const GroupPage = () => { | |
); | ||
|
||
return ( | ||
<VStack align="center"> | ||
<Box w="100%"> | ||
<GroupHeader /> | ||
<GroupSearch | ||
searchValue={searchValue} | ||
handleChange={handleChange} | ||
handleSumbit={handleSumbit} | ||
<> | ||
<TopHeader pathname={'/group'} /> | ||
<div className="mt-[2rem] flex w-full flex-col gap-[1.5rem]"> | ||
<SearchGroup | ||
onClick={() => { | ||
alert('추후 업데이트 될 예정입니다.'); | ||
}} | ||
/> | ||
{isSuccess && | ||
data.pages.map((groups, idx) => { | ||
return <GroupList key={idx} bookGroups={groups.bookGroups} />; | ||
})} | ||
</Box> | ||
<Box ref={ref} /> | ||
<div className="mt-[0.7rem] flex gap-[1rem] overflow-scroll"> | ||
{myGroupsIsSuccess && | ||
myGroupsData.bookGroups.map(group => { | ||
const { title, book, bookGroupId } = group; | ||
return ( | ||
//API isOwner 값이 존재하지 않아 비교하는 로직 추가 필요 | ||
<Link key={bookGroupId} href={`/book/${book.id}`}> | ||
<SimpleBookGroupCard | ||
title={title} | ||
imageSource={book.imageUrl} | ||
isOwner={false} | ||
/> | ||
</Link> | ||
); | ||
})} | ||
</div> | ||
<div className="flex flex-col gap-[1rem]"> | ||
{entireGroupsIsSuccess && | ||
entireGroupsData.pages.map(groups => { | ||
return groups.bookGroups.map(group => { | ||
Comment on lines
+71
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 받아온 데이터를 두 번 풀어서 사용해야 하는 상황에서 반복문을 두 번 돌리는 것이 어색한 것 같아 별도의 방법을 찾아보던 중 |
||
const { | ||
title, | ||
introduce, | ||
book, | ||
startDate, | ||
endDate, | ||
owner, | ||
memberCount, | ||
commentCount, | ||
isPublic, | ||
bookGroupId, | ||
} = group; | ||
return ( | ||
<Link | ||
key={bookGroupId} | ||
className="w-full" | ||
href={`/group/${bookGroupId}`} | ||
> | ||
<DetailBookGroupCard | ||
title={title} | ||
description={introduce} | ||
bookImageSrc={book.imageUrl} | ||
date={{ start: startDate, end: endDate }} | ||
owner={{ | ||
name: owner.nickname, | ||
profileImageSrc: owner.profileUrl, | ||
}} | ||
memberCount={memberCount} | ||
commentCount={commentCount} | ||
isPublic={isPublic} | ||
/> | ||
</Link> | ||
); | ||
}); | ||
})} | ||
</div> | ||
</div> | ||
<div ref={ref} /> | ||
{isFetchingNextPage && <Skeleton w="100%" height="28rem" />} | ||
</VStack> | ||
{/* <Link href={'/group/create'}> | ||
<FloatingButton position="bottom-right" /> | ||
</Link> */} | ||
</> | ||
); | ||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,7 @@ type BookCoverSize = | |
type BookCoverProps = Required< | ||
Pick<ComponentPropsWithoutRef<typeof Image>, 'src'> | ||
> & { | ||
title: string; | ||
title?: string; | ||
size?: BookCoverSize; | ||
}; | ||
Comment on lines
14
to
19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p5; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @minjongbaek There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @minjongbaek There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@WooDaeHyun 따로 참고한 레퍼런스는 없습니다. prop이 HTML 요소에서 지원하는 속성을 위해 사용된다면 DX 측면에서 동일한 이름을 사용하는 것이 좋지 않을까? 라고 생각하는데 이런 생각이 코드에 반영되는 것 같아요. 이 부분은 주관적인 내용을 포함하고 있기에 '누구는 이런 기준으로 prop 명을 정하는구나.' 라고 봐주시면 좋을 것 같아요. 🙇 그렇기 때문에 코멘트에 p5를 적용했습니다. 만약 대현님께서 다른 생각을 가지고 있다면 공유해주시고, 제 피드백을 반영하지 않아도 좋아요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @minjongbaek There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 고민해봤는데 저는 이미지를 보여줄 수 없을 때 |
||
|
||
|
@@ -48,7 +48,7 @@ const BookCover = ({ src, title, size = 'medium' }: BookCoverProps) => { | |
<span className={`relative ${sizeClasses}`}> | ||
<Image | ||
src={src} | ||
alt={title} | ||
alt={title || 'book-cover'} | ||
placeholder="blur" | ||
blurDataURL={DATA_URL['placeholder']} | ||
className="object-fit rounded-[0.5rem] shadow-bookcover" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,17 +2,18 @@ import Image from 'next/image'; | |
|
||
import Badge from '@/ui/Base/Badge'; | ||
import { IconCalendar, IconMembers, IconComments } from '@public/icons'; | ||
import BookCover from '@/v1/book/BookCover'; | ||
|
||
interface DetailBookGroupCardProps { | ||
title: string; | ||
description: string; | ||
book: { title: string; bookImageSrc: string }; | ||
bookImageSrc: string; | ||
date: { start: string; end: string }; | ||
owner: { name: string; profileImageSrc: string }; | ||
owner: { name: string | null; profileImageSrc: string }; | ||
memberCount: number; | ||
commentCount: number; | ||
isPublic: boolean; | ||
handleClick: () => void; | ||
handleClick?: () => void; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p1; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋은 생각입니다. 다른 컴포넌트 handleClick으로 작성된 부분을 일부 수정했었는데 여기도 있었네요! 매의 눈 감사합니다 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분 수정했었으나 Link 태그로 전환하면서 onClick 이벤트는 제거했습니다. 참고 부탁드립니다~ |
||
} | ||
|
||
type BookGroupStatus = 'before' | 'dday' | 'ongoing' | 'end'; | ||
|
@@ -45,7 +46,7 @@ const toDayFromMillseconds = (value: number) => { | |
const DetailBookGroupCard = ({ | ||
title, | ||
description, | ||
book, | ||
bookImageSrc, | ||
date, | ||
owner, | ||
memberCount, | ||
|
@@ -65,31 +66,26 @@ const DetailBookGroupCard = ({ | |
return ( | ||
<div | ||
onClick={handleClick} | ||
className="h-[16.142rem] w-[35.5rem] rounded-[0.4rem] px-[1.6rem] py-[0.9rem] shadow-[0_0_0.6rem_rgba(180,180,180,0.25)]" | ||
className="min-h-[16.142rem] w-full rounded-[0.4rem] px-[1.6rem] py-[0.9rem] shadow-[0_0_0.6rem_rgba(180,180,180,0.25)]" | ||
> | ||
<div className="mb-[1rem] flex gap-[0.5rem]"> | ||
<Dday {...ddayProps}></Dday> | ||
<Public isPublic={isPublic}></Public> | ||
<Dday {...ddayProps} /> | ||
<Public isPublic={isPublic} /> | ||
</div> | ||
<div className="flex gap-[1.4rem]"> | ||
<div className="flex flex-col gap-[0.63rem]"> | ||
<Title title={title}></Title> | ||
<Description description={description}></Description> | ||
<Duration start={date.start} end={date.end}></Duration> | ||
<div className="flex w-[22.5rem] justify-between"> | ||
<Owner | ||
name={owner.name} | ||
profileImageSrc={owner.profileImageSrc} | ||
></Owner> | ||
<div className="flex justify-between gap-[1.5rem]"> | ||
<div className="flex flex-grow flex-col gap-[0.63rem]"> | ||
<Title title={title} /> | ||
<Description description={description} /> | ||
<Duration start={date.start} end={date.end} /> | ||
<div className="flex justify-between"> | ||
<Owner name={owner.name} profileImageSrc={owner.profileImageSrc} /> | ||
<div className="flex gap-[0.5rem]"> | ||
<MemberCount memberCount={memberCount}></MemberCount> | ||
<CommentCount commentCount={commentCount}></CommentCount> | ||
<MemberCount memberCount={memberCount} /> | ||
<CommentCount commentCount={commentCount} /> | ||
</div> | ||
</div> | ||
</div> | ||
<div> | ||
<Book title={book.title} bookImageSrc={book.bookImageSrc}></Book> | ||
</div> | ||
<BookCover src={bookImageSrc} size="medium" /> | ||
</div> | ||
</div> | ||
); | ||
|
@@ -158,7 +154,7 @@ const Title = ({ title }: { title: string }) => { | |
|
||
const Description = ({ description }: { description: string }) => { | ||
return ( | ||
<div className="w-[22.5rem] truncate text-sm"> | ||
<div className="w-[22rem] truncate text-sm"> | ||
<span>{description}</span> | ||
</div> | ||
); | ||
|
@@ -183,14 +179,16 @@ const Owner = ({ | |
name, | ||
profileImageSrc, | ||
}: { | ||
name: string; | ||
name: string | null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ask; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @minjongbaek There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @WooDaeHyun name prop 의 타입을 제가 이해한 내용이 맞다면, 7번 라인에서 선언한 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @minjongbaek There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
현재 APIGroupOwner에서 정의된 owner.name 값이 nullable 한 상태여서 발생한 Error임을 인지하였고 해당 부분을 명시적으로 string 타입으로 지정하여 해당 부분을 수정하기는 했습니다. 확인 부탁드려요~ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. types/group.ts 파일 안에 |
||
profileImageSrc: string; | ||
}) => { | ||
return ( | ||
<div className="flex h-[2rem] gap-[0.5rem]"> | ||
{/* 아바타 컴포넌트로 변경 예정 */} | ||
<div className={'relative h-[2rem] w-[2rem] rounded-full bg-black-400'}> | ||
{profileImageSrc && <Image alt={name} src={profileImageSrc} fill />} | ||
{profileImageSrc && ( | ||
<Image alt={name ? name : ''} src={profileImageSrc} fill /> | ||
)} | ||
</div> | ||
<div className="flex items-center text-xs"> | ||
<span>{name}</span> | ||
|
@@ -224,26 +222,3 @@ const CommentCount = ({ commentCount }: { commentCount: number }) => { | |
</div> | ||
); | ||
}; | ||
|
||
const Book = ({ | ||
bookImageSrc, | ||
title, | ||
}: { | ||
bookImageSrc: string; | ||
title: string; | ||
}) => { | ||
return ( | ||
<div> | ||
<div className="relative h-[10.442rem] w-[8rem]"> | ||
{bookImageSrc && ( | ||
<Image | ||
src={bookImageSrc} | ||
alt={title} | ||
fill | ||
className="object-fit rounded-[0.4rem] shadow-[0.1rem_0.2rem_0.4rem_0_rgba(0,0,0,0.25)]" | ||
/> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ask;
기존에 있던
search1.kakaocdn.net
호스트 외에 두 호스트 에서 받아오는 이미지가 있나요? 👀There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@minjongbaek
네 말씀주신 호스트 외에 독서 모임 목록 페이지에서 위의 두 호스트에서 받아오는 이미지가 존재하여 에러가 발생해 추가로 작성하게 되었습니다! 👀