Skip to content
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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions api/getProduct.ts
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,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keyword 타입이 널러블이여서 undefined가 들어올 수도 있을 것 같은데, 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;
}
};
62 changes: 0 additions & 62 deletions api/itemApi.js

This file was deleted.

93 changes: 93 additions & 0 deletions components/ItemPage/AllItems.module.scss
Copy link
Collaborator

Choose a reason for hiding this comment

The 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;
}
}
139 changes: 139 additions & 0 deletions components/ItemPage/AllItems.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { useEffect, useState } from "react";
import { sortItemsByOrder } from "../../lib/sort";
Copy link
Collaborator

Choose a reason for hiding this comment

The 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");
Copy link
Collaborator

Choose a reason for hiding this comment

The 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"],
Copy link
Collaborator

Choose a reason for hiding this comment

The 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;
10 changes: 10 additions & 0 deletions components/ItemPage/BestItems.module.scss
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);
}
}
}
49 changes: 49 additions & 0 deletions components/ItemPage/BestItems.tsx
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;
Loading
Loading