-
Notifications
You must be signed in to change notification settings - Fork 5
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
[Feature] - 마이페이지 좋아요 탭 및 검색 창 국가 탭 기능 구현 #556
Changes from all commits
bd935f9
7e94a10
3018ff4
560ea7a
bdf6ba7
c23ef37
574fe3b
9c38e9c
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 |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import styled from "@emotion/styled"; | ||
|
||
export const Layout = styled.div` | ||
display: flex; | ||
width: 100%; | ||
height: 100%; | ||
gap: ${(props) => props.theme.spacing.m}; | ||
`; | ||
|
||
export const List = styled.ul` | ||
display: flex; | ||
flex-direction: column; | ||
gap: ${(props) => props.theme.spacing.m}; | ||
|
||
width: 100%; | ||
padding: 0 ${(props) => props.theme.spacing.l}; | ||
`; | ||
|
||
export const Container = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
align-items: flex-start; | ||
gap: ${(props) => props.theme.spacing.s}; | ||
`; | ||
|
||
export const DetailContainer = styled.div` | ||
display: flex; | ||
align-items: center; | ||
gap: ${(props) => props.theme.spacing.m}; | ||
|
||
width: 100%; | ||
`; | ||
|
||
export const BoxButton = styled.button` | ||
display: flex; | ||
gap: ${(props) => props.theme.spacing.m}; | ||
justify-content: flex-start; | ||
align-items: center; | ||
|
||
width: 100%; | ||
padding: ${(props) => props.theme.spacing.m}; | ||
border: 1px solid ${(props) => props.theme.colors.border}; | ||
border-radius: 10px; | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { useEffect } from "react"; | ||
import { useNavigate } from "react-router-dom"; | ||
|
||
import { css } from "@emotion/react"; | ||
|
||
import type { UserResponse } from "@type/domain/user"; | ||
|
||
import useInfiniteMyLikes from "@queries/useInfiniteMyLikes"; | ||
|
||
import { AvatarCircle, Text } from "@components/common"; | ||
import MyPageTabContentSkeleton from "@components/pages/my/MyPageTabContentSkeleton/MyPageTabContentSkeleton"; | ||
|
||
import useIntersectionObserver from "@hooks/useIntersectionObserver"; | ||
|
||
import { ERROR_MESSAGE_MAP } from "@constants/errorMessage"; | ||
import { ROUTE_PATHS_MAP } from "@constants/route"; | ||
|
||
import theme from "@styles/theme"; | ||
|
||
import MyPageTabContent from "../MyPageTabContent/MyPageTabContent"; | ||
import * as S from "./MyLikes.styled"; | ||
|
||
interface MyLikesProps { | ||
userData: UserResponse; | ||
} | ||
|
||
const MyLikes = ({ userData }: MyLikesProps) => { | ||
const { myLikes, fetchNextPage, status, isPaused, error } = useInfiniteMyLikes(userData); | ||
const { lastElementRef } = useIntersectionObserver(fetchNextPage); | ||
|
||
const navigate = useNavigate(); | ||
|
||
const handleClickTravelogue = (id: string) => { | ||
navigate(ROUTE_PATHS_MAP.travelogue(Number(id))); | ||
}; | ||
|
||
const handleClickIconButton = () => { | ||
navigate(ROUTE_PATHS_MAP.root); | ||
}; | ||
|
||
useEffect(() => { | ||
if (isPaused) alert(ERROR_MESSAGE_MAP.network); | ||
}, [isPaused]); | ||
|
||
if (status === "pending") return <MyPageTabContentSkeleton />; | ||
|
||
if (error) alert(error.message); | ||
|
||
return ( | ||
<> | ||
<MyPageTabContent | ||
iconButtonType="search-icon" | ||
iconButtonLabel="다른 여행기 구경하기" | ||
onClickIconButton={handleClickIconButton} | ||
contentDetail={myLikes} | ||
renderItem={({ id, title, createdAt, thumbnailUrl, authorName }) => ( | ||
<S.Layout onClick={() => handleClickTravelogue(id)}> | ||
<AvatarCircle size="medium" profileImageUrl={thumbnailUrl} /> | ||
|
||
<S.Container> | ||
<Text | ||
textType="body" | ||
css={css` | ||
font-weight: 500; | ||
`} | ||
> | ||
{title} | ||
</Text> | ||
<S.DetailContainer> | ||
<Text | ||
textType="detail" | ||
css={css` | ||
color: ${theme.colors.text.secondary}; | ||
`} | ||
> | ||
{authorName} | ||
</Text> | ||
<Text | ||
textType="detail" | ||
css={css` | ||
color: ${theme.colors.text.secondary}; | ||
`} | ||
> | ||
{createdAt} | ||
</Text> | ||
</S.DetailContainer> | ||
</S.Container> | ||
</S.Layout> | ||
)} | ||
/> | ||
|
||
<div | ||
ref={lastElementRef} | ||
css={css` | ||
height: 1px; | ||
`} | ||
/> | ||
</> | ||
); | ||
}; | ||
|
||
export default MyLikes; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,13 @@ | ||
import { ERROR_MESSAGE_MAP } from "@constants/errorMessage"; | ||
|
||
import MyLikes from "./MyLikes/MyLikes"; | ||
import MyTravelPlans from "./MyTravelPlans/MyTravelPlans"; | ||
import MyTravelogues from "./MyTravelogues/MyTravelogues"; | ||
|
||
export const TAB_CONTENT = [ | ||
{ label: "✈️ 내 여행 계획", component: MyTravelPlans }, | ||
{ label: "📝 내 여행기", component: MyTravelogues }, | ||
{ label: "❤️ 좋아요", component: MyLikes }, | ||
] as const; | ||
|
||
export const IGNORED_ERROR_MESSAGES = [ERROR_MESSAGE_MAP.api.login, "Network Error"]; |
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. 분리 좋네요! 👍 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { SearchFallback, Text } from "@components/common"; | ||
|
||
import * as S from "./TravelogueList.styled"; | ||
|
||
interface EmptySearchResultProps { | ||
keyword: string | undefined; | ||
} | ||
|
||
const EmptySearchResult = ({ keyword }: EmptySearchResultProps) => { | ||
return ( | ||
<S.Layout> | ||
{keyword && ( | ||
<Text css={S.searchResultTextStyle} textType="title">{`"${keyword}" 검색 결과`}</Text> | ||
)} | ||
<S.SearchFallbackWrapper> | ||
<SearchFallback title="휑" text="검색 결과가 없어요." /> | ||
</S.SearchFallbackWrapper> | ||
</S.Layout> | ||
); | ||
}; | ||
|
||
export default EmptySearchResult; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
export const TAB_CONTENT = [ | ||
{ label: "제목", searchType: "TITLE" }, | ||
{ label: "작성자", searchType: "AUTHOR" }, | ||
{ label: "나라", searchType: "COUNTRY" }, | ||
] as const; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,7 @@ export const QUERY_KEYS_MAP = { | |
selectedSortingOption, | ||
selectedTravelPeriodOption, | ||
], | ||
likes: () => [...QUERY_KEYS_MAP.travelogue.all, "likes"], | ||
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. 이 친구는 메서드로 만든 이유가 있을까요? 제가 보기엔 값으로 만들어도 충분하다는 생각이어서요! 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. 다른 부분들이 이러한 방식으로 구성되어 있어서 일관성을 위해서 해당 방식을 적용했습니다 |
||
}, | ||
travelPlan: { | ||
all: ["travel-plans"], | ||
|
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.
요 녀석도 없어도 동일하게 동작합니다!
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.
justify-content
의 기본값이flex-start
라서 그런거 같네요~!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.
삭제했습니다!