-
Notifications
You must be signed in to change notification settings - Fork 79
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
[이종욱] Sprint12 #736
The head ref may contain hidden characters: "Next.js-\uC774\uC885\uC6B1-sprint12"
[이종욱] Sprint12 #736
Changes from all commits
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,25 @@ | ||
import axios from "./axios"; | ||
|
||
interface GetProductsQueries { | ||
page?: number; | ||
pageSize?: number; | ||
orderBy?: "recent" | "favorite"; | ||
keyword?: string; | ||
} | ||
|
||
export const getProducts = async ({ | ||
page = 1, | ||
pageSize = 3, | ||
orderBy = "recent", | ||
keyword, | ||
}: GetProductsQueries) => { | ||
try { | ||
const response = await axios.get("/products", { | ||
params: { page, pageSize, orderBy, keyword }, | ||
}); | ||
return response.data; | ||
} catch (error) { | ||
console.error(`Failed to fetch Data: ${error}`); | ||
throw error; | ||
} | ||
}; |
This file was deleted.
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. 저는 개인적으로 class 내부에 미디어쿼리를 넣는 방법을 선호합니다. 이렇게 하면 유지보수하는 시점에 수정해야하는 클래스 하나를 찾고 내부의 반응형 css를 수정해줄 수 있기 때문입니다! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
@import "/styles/index.scss"; | ||
|
||
.title { | ||
display: flex; | ||
align-items: center; | ||
justify-content: space-between; | ||
gap: 10px; | ||
padding-block: 24px 12px; | ||
|
||
.content { | ||
@include font-base(get_color(black), 700, 20px, 28px); | ||
max-width: 600px; | ||
flex-grow: 3; | ||
} | ||
|
||
.search { | ||
height: 42px; | ||
display: flex; | ||
align-items: center; | ||
gap: 3px; | ||
background-color: get_color(gray_100); | ||
border-radius: 12px; | ||
padding-block: 9px; | ||
padding-inline: 20px 16px; | ||
|
||
.search_image { | ||
width: 16px; | ||
height: 16px; | ||
background-size: 16px; | ||
background-repeat: no-repeat; | ||
background-image: url("../../src/assets/images/icons/ic_search.svg"); | ||
} | ||
|
||
.search_input { | ||
@include font-base(get_color(black), 400, 16px, 24px); | ||
background-color: get_color(gray_100); | ||
border: none; | ||
} | ||
} | ||
|
||
.add_item { | ||
@include font-base(get_color(white), 600, 16px, 19.09px); | ||
text-align: center; | ||
width: 133px; | ||
height: 42px; | ||
border-radius: 8px; | ||
padding: 12px 10px; | ||
background-color: get_color(blue_primary); | ||
text-decoration: none; | ||
flex-shrink: 0; | ||
} | ||
.select_box_area { | ||
position: relative; | ||
max-width: 64px; | ||
background-color: get_color(white); | ||
padding: 3px; | ||
border-radius: 3px; | ||
right: 0; | ||
} | ||
} | ||
|
||
@media screen and (min-width: 768px) and (max-width: 1199px) { | ||
.search { | ||
width: 242px; | ||
} | ||
} | ||
|
||
@media screen and (min-width: 375px) and (max-width: 767px) { | ||
.title { | ||
display: grid; | ||
grid-template: repeat(2, auto) / 300px auto; | ||
|
||
.content { | ||
width: fit-content; | ||
grid-column: 1 / span 1; | ||
grid-row: 1 / span 1; | ||
} | ||
|
||
.search { | ||
grid-column: 1 / span 1; | ||
grid-row: 2 / span 1; | ||
} | ||
.add_item { | ||
grid-column: 2 / span 1; | ||
grid-row: 1 / span 1; | ||
} | ||
} | ||
|
||
.select_box_area { | ||
grid-column: 2 / span 1; | ||
grid-row: 2 / span 1; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import { useEffect, useState } from "react"; | ||
import { sortItemsByOrder } from "../../lib/sort"; | ||
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. alias path 도입해보시는것도 추천드려요! |
||
import { AllProducts } from "./Products"; | ||
import styles from "./AllItems.module.scss"; | ||
import SelectMenu from "./SelectMenu"; | ||
import Link from "next/link"; | ||
import { ProductSortOption } from "../../types/productTypes"; | ||
import useWindowSize from "../../src/hooks/useWindowSize"; | ||
import { FieldValues, SubmitHandler, useForm } from "react-hook-form"; | ||
import { getProducts } from "../../api/getProduct"; | ||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; | ||
|
||
function AllItems() { | ||
const [order, setOrder] = useState<ProductSortOption>("recent"); | ||
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. recent 같은 값은 상수로 활용해보시는 것을 추천드립니다. |
||
const queryClient = useQueryClient(); | ||
const windowWidth = useWindowSize(); | ||
const { register, watch, handleSubmit, setValue, getValues } = useForm(); | ||
const [pageSize, setPageSize] = useState<number>(10); | ||
const [keyword, setKeyword] = useState<string>(""); | ||
|
||
const { | ||
data: itemsData, | ||
isPending, | ||
isError, | ||
} = useQuery({ | ||
queryKey: ["allitems"], | ||
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. queryKey에 상태값도 포함해주셔야 다른 상태를 불러올때 캐싱된 값을 불러오지 않고 새롭게 요청을 보낼 수 있습니다. |
||
queryFn: () => getProducts({ pageSize, orderBy: order }), | ||
retry: 0, | ||
}); | ||
|
||
const items = itemsData?.list ?? []; | ||
|
||
const addKeywordMutation = useMutation({ | ||
mutationFn: (keyword: string) => | ||
getProducts({ pageSize, orderBy: order, keyword }), | ||
onSuccess: () => { | ||
queryClient.invalidateQueries({ | ||
queryKey: ["allitems"], | ||
}); | ||
}, | ||
}); | ||
|
||
const addOrderMutation = useMutation({ | ||
mutationFn: (order: ProductSortOption) => | ||
getProducts({ pageSize, orderBy: order, keyword }), | ||
onSuccess: () => { | ||
queryClient.invalidateQueries({ | ||
queryKey: ["allitems"], | ||
}); | ||
}, | ||
}); | ||
|
||
const handleKeyword = (keyword: string) => { | ||
addKeywordMutation.mutate(keyword); | ||
}; | ||
|
||
const handleOrder = (order: ProductSortOption) => { | ||
addKeywordMutation.mutate(order); | ||
setOrder(order); | ||
}; | ||
|
||
const sortedItems = sortItemsByOrder(items, order); | ||
|
||
const onSubmit: SubmitHandler<FieldValues> = async (data) => { | ||
if (data.keyword) { | ||
setKeyword(data.keyword); | ||
} | ||
handleKeyword(keyword); | ||
}; | ||
|
||
useEffect(() => { | ||
if (windowWidth < 768) { | ||
setPageSize(4); | ||
} else if (windowWidth < 1199) { | ||
setPageSize(6); | ||
} else { | ||
setPageSize(10); | ||
} | ||
}, [windowWidth]); | ||
|
||
// useEffect(() => { | ||
// async ({ | ||
// pageSize, | ||
// order, | ||
// keyword, | ||
// }: { | ||
// pageSize: number; | ||
// order: ProductSortOption; | ||
// keyword: string; | ||
// }) => { | ||
// let result = await getProducts({ pageSize, orderBy: order, keyword }); | ||
// const { list } = result; | ||
// setAllItems(list); | ||
// }; | ||
// // eslint-disable-next-line react-hooks/exhaustive-deps | ||
// }, [pageSize, order, keyword]); | ||
|
||
return ( | ||
<> | ||
<div className={styles.container}> | ||
<div className={styles.title}> | ||
<h2 className={styles.content}> | ||
{windowWidth < 1199 ? "판매 중인 상품" : "전체 상품"} | ||
</h2> | ||
<div className={styles.search}> | ||
<span className={styles.search_image} /> | ||
<form onSubmit={handleSubmit(onSubmit)}> | ||
<input | ||
{...register("keyword")} | ||
type="search" | ||
placeholder="검색할 상품을 입력해주세요" | ||
className={styles.search_input} | ||
/> | ||
</form> | ||
</div> | ||
<Link href="/additem" className={styles.add_item}> | ||
상품 등록하기 | ||
</Link> | ||
<span className={styles.select_box_area}> | ||
<SelectMenu order={order} setOrder={handleOrder} /> | ||
</span> | ||
</div> | ||
<div> | ||
<AllProducts items={sortedItems} counts={pageSize} /> | ||
</div> | ||
{/* {isError && <span>{loadingError.message}</span>} */} | ||
</div> | ||
{/* <Paging | ||
itemsCount={1} | ||
totalPageCount={totalPage} | ||
displayCount={pageSize} | ||
order={order} | ||
onClick={handleLoad} | ||
/> */} | ||
</> | ||
); | ||
} | ||
|
||
export default AllItems; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
@import "/styles/index.scss"; | ||
|
||
.container { | ||
.title { | ||
padding-block: 24px 12px; | ||
.content { | ||
@include font-base(get_color(gray_800), 700, 20px, 28px); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { useEffect, useState } from "react"; | ||
import { BestProducts } from "./Products"; | ||
import { sortItemsByOrder } from "../../lib/sort"; | ||
import styles from "./BestItems.module.scss"; | ||
import useWindowSize from "../../src/hooks/useWindowSize"; | ||
import { useQuery, useQueryClient } from "@tanstack/react-query"; | ||
import { getProducts } from "../../api/getProduct"; | ||
|
||
function BestItems() { | ||
const windowWidth = useWindowSize(); | ||
const [pageSize, setPageSize] = useState(4); | ||
|
||
const { | ||
data: itemsData, | ||
isPending, | ||
isError, | ||
} = useQuery({ | ||
queryKey: ["bestitems"], | ||
queryFn: () => getProducts({ pageSize, orderBy: "favorite" }), | ||
retry: 0, | ||
}); | ||
|
||
// const items = itemsData?.results ?? []; | ||
const items = itemsData?.list ?? []; | ||
|
||
const sortedItems = sortItemsByOrder(items, "favorite"); | ||
|
||
useEffect(() => { | ||
if (windowWidth < 768) { | ||
setPageSize(1); | ||
} else if (windowWidth < 1199) { | ||
setPageSize(2); | ||
} else { | ||
setPageSize(4); | ||
} | ||
}, [windowWidth]); | ||
|
||
return ( | ||
<div className={styles.container}> | ||
<div className={styles.title}> | ||
<h2 className={styles.content}>베스트 상품</h2> | ||
</div> | ||
<BestProducts items={sortedItems} counts={pageSize} /> | ||
{/* {loadingError?.message && <span>{loadingError.message}</span>} */} | ||
</div> | ||
); | ||
} | ||
|
||
export default BestItems; |
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.
keyword 타입이 널러블이여서 undefined가 들어올 수도 있을 것 같은데, keyword 타입을 명시해주시거나 빈문자열로 초기화해주시는 것도 좋을 것 같습니다.