Skip to content

Commit

Permalink
Feat/#155 메인 페이지 카테고리 목록 + 상품 api 연동 (#168)
Browse files Browse the repository at this point in the history
  • Loading branch information
chojooyoung authored Dec 22, 2023
2 parents aa232b6 + 39dfb6b commit 2ba5e91
Show file tree
Hide file tree
Showing 22 changed files with 239 additions and 335 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"postcss": "^8.4.27",
"postcss-syntax": "^0.36.2",
"prettier": "2.7.1",
"react-intersection-observer": "^9.5.3",
"storybook": "^7.5.1",
"stylelint": "^15.10.2",
"stylelint-config-standard": "^34.0.0",
Expand Down
7 changes: 6 additions & 1 deletion src/apis/post/apis.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import type { GetCategoriesRes } from './types'
import type { GetCategoriesRes, PostReq, PostDataInfoRes } from './types'

import { http } from '@utils/http'

export const getCategories = () =>
http.get<null, GetCategoriesRes>('/categories')

export const getPostList = (param: PostReq) => {
return http.get<PostReq, PostDataInfoRes>('/posts', param)
}
16 changes: 14 additions & 2 deletions src/apis/post/queries.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import { useQuery } from '@tanstack/react-query'
import { getCategories } from './apis'
import { useInfiniteQuery, useQuery } from '@tanstack/react-query'
import { getCategories, getPostList } from './apis'
import type { PostReq, PostDataInfoRes } from './types'

export const useGetCategoriesQuery = () =>
useQuery({
queryKey: ['getCategories'],
queryFn: getCategories
})

export const useGetPostListQuery = (param: PostReq) =>
useInfiniteQuery<PostDataInfoRes>({
queryKey: ['postList'],
queryFn: () => getPostList(param),
initialPageParam: null,
getNextPageParam: lastPage =>
lastPage?.hasNext
? lastPage.posts[lastPage.posts.length - 1].id
: undefined
})
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
105 changes: 23 additions & 82 deletions src/components/home/CategorySlider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,7 @@ import { useMedia } from '@offer-ui/react'
import { useState, useEffect, useRef, useCallback } from 'react'
import type { ReactElement, TouchEventHandler } from 'react'
import { Styled } from './styled'
import { IMAGE } from '@constants'

const CATEGORY_LIST = [
{
imageUrl: `${IMAGE.CATEGORY_MAN_GOODS}`,
title: '남성패션/잡화',
url: 'string'
},
{
imageUrl: `${IMAGE.CATEGORY_WOMAN_GOODS}`,
title: '여성패션/잡화',
url: 'string'
},
{
imageUrl: `${IMAGE.CATEGORY_GAME}`,
title: '게임',
url: 'string'
},
{
imageUrl: `${IMAGE.CATEGORY_SPORTS}`,
title: '스포츠/레저',
url: 'string'
},
{
imageUrl: `${IMAGE.CATEGORY_TOY}`,
title: '장난감/취미',
url: 'string'
},
{
imageUrl: `${IMAGE.CATEGORY_DIGITAL_DEVICE}`,
title: '디지털기기',
url: 'string'
},
{
imageUrl: `${IMAGE.CATEGORY_CAR}`,
title: '자동차/공구',
url: 'string'
},
{
imageUrl: `${IMAGE.CATEGORY_APPLIANCE}`,
title: '생활가전',
url: 'string'
},
{
imageUrl: `${IMAGE.CATEGORY_DIGITAL_FURNITURE}`,
title: '가구/인테리어',
url: 'string'
},
{
imageUrl: `${IMAGE.CATEGORY_BOOK}`,
title: '도서/티켓/음반',
url: 'string'
},
{
imageUrl: `${IMAGE.CATEGORY_PET_GOODS}`,
title: '반려동물용품',
url: 'string'
},
{
imageUrl: `${IMAGE.CATEGORY_MORE}`,
title: '더보기',
url: 'string'
}
]
import { useGetCategoriesQuery } from '@apis/post'

const CategorySlider = (): ReactElement => {
const containerRef = useRef<HTMLDivElement | null>(null)
Expand All @@ -77,6 +14,8 @@ const CategorySlider = (): ReactElement => {
const [isMoveFromArrowButton, setIsMoveArrowButton] = useState<number>(0)
const isFirstCategory = containerRef.current?.scrollLeft === 0

const { data: categories } = useGetCategoriesQuery()

useEffect(() => {
if (desktop) {
setIsDesktop(true)
Expand Down Expand Up @@ -169,24 +108,26 @@ const CategorySlider = (): ReactElement => {
)}
<Styled.CateGoryBoxWrapper
isMoveFromArrowButton={isMoveFromArrowButton}>
{CATEGORY_LIST.map(cateGory => (
<Styled.CategoryItem
key={cateGory.title}
onClick={(): void => {
alert(cateGory.title)
}}>
<Styled.CategoryImgWrapper>
<Styled.CategoryImg
key={cateGory.title}
alt={`category-${cateGory.title}`}
height={58}
src={cateGory.imageUrl}
width={58}
/>
</Styled.CategoryImgWrapper>
<Styled.CateGoryName>{cateGory.title}</Styled.CateGoryName>
</Styled.CategoryItem>
))}
{categories?.map(cateGory => {
return (
<Styled.CategoryItem
key={cateGory.name}
onClick={(): void => {
alert(cateGory.name)
}}>
<Styled.CategoryImgWrapper>
<Styled.CategoryImg
key={cateGory.name}
alt={`category-${cateGory.name}`}
height={58}
src={cateGory.imageUrl}
width={58}
/>
</Styled.CategoryImgWrapper>
<Styled.CateGoryName>{cateGory.name}</Styled.CateGoryName>
</Styled.CategoryItem>
)
})}
</Styled.CateGoryBoxWrapper>
</Styled.CateGoryBox>
</Styled.CateGoryWrapper>
Expand Down
2 changes: 1 addition & 1 deletion src/components/home/CategorySlider/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const CateGoryBoxWrapper = styled.div<CateGoryBoxWrapperProps>`
width: 100%;
max-width: 1200px;
height: 118px;
height: fit-content;
cursor: pointer;
Expand Down
7 changes: 4 additions & 3 deletions src/components/home/ProductItem/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ReactElement } from 'react'
import { Styled } from './styled'
import type { ProductItemProps } from './types'
import { getTimeDiffText } from '@utils/format'

const ProductItem = ({ productItem }: ProductItemProps): ReactElement => {
return (
Expand All @@ -10,8 +11,8 @@ const ProductItem = ({ productItem }: ProductItemProps): ReactElement => {
<Styled.ProductImg
key={productItem.id}
alt={`productName-${productItem.title}`}
maxWidth="276px"
src={productItem.mainImageUrl}
src={productItem.thumnail}
style={{ maxWidth: '276px' }}
/>
<Styled.HeartButton
icon="heart"
Expand All @@ -27,7 +28,7 @@ const ProductItem = ({ productItem }: ProductItemProps): ReactElement => {
{productItem.price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
</Styled.ProductItemPrice>
<Styled.ProductItemAddress>
{productItem.tradeArea} 방금전
{productItem.location} {getTimeDiffText(productItem.createdAt)}
</Styled.ProductItemAddress>
</div>
</>
Expand Down
16 changes: 4 additions & 12 deletions src/components/home/ProductItem/types.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
export type ProductItemProps = {
productItem: {
id: number
mainImageUrl: string
title: string
price: number
tradeArea: string
tradeStatus: {
code: number
name: string
}
createdDate: string
modifiedDate: string
isLiked: boolean
likeCount: number
isReviewed: boolean
sellerNickName: string
thumnail: string
location: string
createdAt: string
liked: boolean
}
}
36 changes: 32 additions & 4 deletions src/components/home/ProductList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,45 @@
import type { ReactElement } from 'react'
import { useEffect, useState } from 'react'
import { useInView } from 'react-intersection-observer'
import { Styled } from './styled'
import type { ProductListProps } from './types'
import { ProductItem } from '../ProductItem'

const ProductList = ({ productList }: ProductListProps): ReactElement => {
const ProductList = ({
postData,
hasNextPage,
fetchNextPage
}: ProductListProps): ReactElement => {
const [isFirstRender, setIsFirstRender] = useState<boolean>(false)
const { ref: isLastPrdRef, inView } = useInView({
threshold: 1
})

const shouldFetchNextPage = inView && hasNextPage

useEffect(() => {
if (!shouldFetchNextPage) {
return
}
if (!isFirstRender) {
setIsFirstRender(true)
return
}

fetchNextPage && fetchNextPage()
}, [fetchNextPage, shouldFetchNextPage, isFirstRender])

return (
<>
<Styled.NewProductTitle>새로운 상품</Styled.NewProductTitle>
<Styled.ProductListWrapper>
{productList.map(item => (
<ProductItem key={item.id} productItem={item} />
))}
{postData?.map(page =>
page?.posts?.map(item => (
<ProductItem key={item.id} productItem={item} />
))
)}
</Styled.ProductListWrapper>
<Styled.LastFooter ref={isLastPrdRef} />
</>
)
}
Expand Down
7 changes: 6 additions & 1 deletion src/components/home/ProductList/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ const ProductListWrapper = styled.div`
}
`

const LastFooter = styled.div`
height: 20px;
`

export const Styled = {
NewProductTitle,
ProductListWrapper
ProductListWrapper,
LastFooter
}
32 changes: 15 additions & 17 deletions src/components/home/ProductList/types.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import type {
FetchNextPageOptions,
InfiniteData,
InfiniteQueryObserverResult
} from '@tanstack/react-query'
import type { PostReq, PostDataInfoRes } from '@apis/post'

export type ProductListProps = {
productList: {
id: number
mainImageUrl: string
title: string
price: number
tradeArea: string
tradeStatus: {
code: number
name: string
}
createdDate: string
modifiedDate: string
isLiked: boolean
likeCount: number
isReviewed: boolean
sellerNickName: string
}[]
postData?: PostDataInfoRes[]
filterOption?: Pick<PostReq, 'sort' | 'category' | 'minPrice' | 'maxPrice'>
hasNextPage?: boolean
fetchNextPage?(
options?: FetchNextPageOptions
): Promise<
InfiniteQueryObserverResult<InfiniteData<PostDataInfoRes, unknown>, Error>
>
}
28 changes: 14 additions & 14 deletions src/constants/images.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import CATEGORY_APPLIANCE from '@assets/images/category_appliance.svg'
import CATEGORY_BOOK from '@assets/images/category_book.svg'
import CATEGORY_BOOKS from '@assets/images/category_books.svg'
import CATEGORY_CAR from '@assets/images/category_car.svg'
import CATEGORY_DIGITAL_DEVICE from '@assets/images/category_ditigaldevice.svg'
import CATEGORY_DIGITAL_FURNITURE from '@assets/images/category_furniture.svg'
import CATEGORY_DIGITAL from '@assets/images/category_digital.svg'
import CATEGORY_FURNITURE from '@assets/images/category_furniture.svg'
import CATEGORY_GAME from '@assets/images/category_game.svg'
import CATEGORY_MAN_GOODS from '@assets/images/category_mangoods.svg'
import CATEGORY_MORE from '@assets/images/category_more.svg'
import CATEGORY_PET_GOODS from '@assets/images/category_petgoods.svg'
import CATEGORY_MEN_FASHION from '@assets/images/category_menfashion.svg'
import CATEGORY_OTHER from '@assets/images/category_other.svg'
import CATEGORY_PET from '@assets/images/category_pet.svg'
import CATEGORY_SPORTS from '@assets/images/category_sports.svg'
import CATEGORY_TOY from '@assets/images/category_toy.svg'
import CATEGORY_WOMAN_GOODS from '@assets/images/category_womangoods.svg'
import CATEGORY_WOMEN_FASHION from '@assets/images/category_womanfashion.svg'
import CHECKBOARD from '@assets/images/checkboard.svg'
import LOGO from '@assets/images/logo.svg'
import MAIL from '@assets/images/mail.svg'
Expand All @@ -19,17 +19,17 @@ type ImageKey = keyof typeof IMAGE

export const IMAGE = {
CATEGORY_APPLIANCE,
CATEGORY_BOOK,
CATEGORY_BOOKS,
CATEGORY_CAR,
CATEGORY_DIGITAL_DEVICE,
CATEGORY_DIGITAL_FURNITURE,
CATEGORY_DIGITAL,
CATEGORY_FURNITURE,
CATEGORY_GAME,
CATEGORY_MAN_GOODS,
CATEGORY_MORE,
CATEGORY_PET_GOODS,
CATEGORY_MEN_FASHION,
CATEGORY_OTHER,
CATEGORY_PET,
CATEGORY_SPORTS,
CATEGORY_TOY,
CATEGORY_WOMAN_GOODS,
CATEGORY_WOMEN_FASHION,
CHECKBOARD,
LOGO,
MAIL,
Expand Down
Loading

0 comments on commit 2ba5e91

Please sign in to comment.