diff --git a/.gitignore b/.gitignore index 4d29575de..3a894327c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +#file +visualwind.wrapper.tsx + # dependencies /node_modules /.pnp diff --git a/src/App.jsx b/src/App.jsx index 8cd05c49f..d41047767 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -19,7 +19,10 @@ function App() { } /> */} } /> } /> - } /> + + } /> + } /> + } /> diff --git a/src/assets/images/icons/ic_X.svg b/src/assets/images/icons/ic_X.svg new file mode 100644 index 000000000..522eca44f --- /dev/null +++ b/src/assets/images/icons/ic_X.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/icons/ic_union.svg b/src/assets/images/icons/ic_union.svg new file mode 100644 index 000000000..f4ada2f05 --- /dev/null +++ b/src/assets/images/icons/ic_union.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/items/BestItems/BestItems.jsx b/src/components/items/BestItems/BestItems.jsx index 5c1b75bd3..081d270f1 100644 --- a/src/components/items/BestItems/BestItems.jsx +++ b/src/components/items/BestItems/BestItems.jsx @@ -22,17 +22,24 @@ function BestItems() { const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(4); const [keyword, setKeyword] = useState(''); + // 에러 + const [fetchingError, setfetchingError] = useState(null); const fetchItemList = async ({ order, page, pageSize, keyword }) => { - let products = await getProducts({ order, page, pageSize, keyword }); - setItemList(products.list); + try { + setfetchingError(null); + const products = await getProducts({ order, page, pageSize, keyword }); + setItemList(products.list); + } catch (err) { + setItemList([]); + setfetchingError(err); + } + }; + const handleResize = () => { + setPageSize(getPageSize()); }; useEffect(() => { - const handleResize = () => { - setPageSize(getPageSize()); - }; - window.addEventListener('resize', handleResize); fetchItemList({ order, page, pageSize, keyword }); @@ -47,6 +54,7 @@ function BestItems() {
베스트 상품
+ {fetchingError && fetchingError.message} {itemList?.map((item) => ( ))} diff --git a/src/components/items/PaginationBar/PaginatinoBar.css b/src/components/items/PaginationBar/PaginatinoBar.css new file mode 100644 index 000000000..a2cb0298f --- /dev/null +++ b/src/components/items/PaginationBar/PaginatinoBar.css @@ -0,0 +1,33 @@ +.pagination-bar { + display: flex; + align-items: center; + justify-content: center; +} + +.btn-pagination { + border: 1px solid #e5e7eb; + border-radius: 50%; + width: 40px; + height: 40px; + color: #6b7280; + font-weight: 600; + font-size: 16px; + display: flex; + align-items: center; + justify-content: center; + margin-right: 20px; +} + +.btn-pagination:last-child { + margin-right: 0; +} + +.btn-pagination:disabled { + cursor: default; + opacity: 0.5; +} + +.btn-pagination.active { + background-color: var(--blue); + color: #fff; +} diff --git a/src/components/items/PaginationBar/PaginationBar.jsx b/src/components/items/PaginationBar/PaginationBar.jsx new file mode 100644 index 000000000..4060b546a --- /dev/null +++ b/src/components/items/PaginationBar/PaginationBar.jsx @@ -0,0 +1,55 @@ +import React from 'react'; +import './PaginatinoBar.css'; +import { ReactComponent as IconArrowLeft } from '../../../assets/images/icons/arrow_left.svg'; +import { ReactComponent as IconArrowRight } from '../../../assets/images/icons/arrow_right.svg'; + +function PaginationBar({ totalPageNum, activePageNum, onPageChange }) { + const maxVisiblePages = 5; + let startPage; + + if (totalPageNum <= maxVisiblePages) { + startPage = 1; + } else { + startPage = Math.max(activePageNum - Math.floor(maxVisiblePages / 2), 1); + startPage = Math.min(startPage, totalPageNum - maxVisiblePages + 1); + } + + const pages = Array.from( + { length: Math.min(maxVisiblePages, totalPageNum - startPage + 1) }, + (_, i) => startPage + i + ); + return ( + <> +
+ + {/* 반복 */} + {pages.map((page) => ( + + ))} + +
+ + ); +} + +export default PaginationBar; diff --git a/src/components/items/SailItems/SailItems.css b/src/components/items/SellingItems/SellingItems.css similarity index 100% rename from src/components/items/SailItems/SailItems.css rename to src/components/items/SellingItems/SellingItems.css diff --git a/src/components/items/SailItems/SailItems.jsx b/src/components/items/SellingItems/SellingItems.jsx similarity index 67% rename from src/components/items/SailItems/SailItems.jsx rename to src/components/items/SellingItems/SellingItems.jsx index 4582f9b6e..4b063a2bb 100644 --- a/src/components/items/SailItems/SailItems.jsx +++ b/src/components/items/SellingItems/SellingItems.jsx @@ -1,9 +1,10 @@ -import './SailItems.css'; import { ReactComponent as IconSearch } from '../../../assets/images/icons/ic_search.svg'; import { ReactComponent as IconSort } from '../../../assets/images/icons/ic_sort.svg'; import { useEffect, useState } from 'react'; import { getProducts } from '../../../pages/api/Items'; import Item from '../Item/Item'; +import PaginationBar from '../PaginationBar/PaginationBar'; +import './SellingItems.css'; const getPageSize = () => { const width = window.innerWidth; @@ -16,7 +17,7 @@ const getPageSize = () => { } }; -function SailItems() { +function SellingItems() { // 상품 const [itemList, setItemList] = useState([]); // 쿼리 @@ -26,7 +27,14 @@ function SailItems() { const [keyword, setKeyword] = useState(''); // 검색 const [isDropdownVisible, setIsDropdownVisible] = useState(false); + // 페이지네이션 + const [totalPageNum, setTotalPageNum] = useState(); + // 에러 + const [fetchingError, setfetchingError] = useState(null); + const handlePageChange = (pageNumber) => { + setPage(pageNumber); + }; const handleClickDropdown = () => { setIsDropdownVisible(!isDropdownVisible); }; @@ -42,8 +50,16 @@ function SailItems() { }; const fetchItemList = async ({ order, page, pageSize, keyword }) => { - let products = await getProducts({ order, page, pageSize, keyword }); - setItemList(products.list); + try { + setfetchingError(null); + const products = await getProducts({ order, page, pageSize, keyword }); + setItemList(products.list); + setTotalPageNum(Math.ceil(products.totalCount / pageSize)); + } catch (err) { + setItemList([]); + setTotalPageNum(0); + setfetchingError(err); + } }; useEffect(() => { @@ -60,15 +76,15 @@ function SailItems() { return ( <> -
+
{/* 판매 중인 상품 헤더 */} -
-
-
판매 중인 상품
+
+
+
판매 중인 상품
- {/* 판매 중인 상품 목록 */} -
+ {fetchingError && ( +
+ {fetchingError.message} +
+ )} +
{itemList?.map((item) => ( - + ))}
+
+ +
); } -export default SailItems; +export default SellingItems; diff --git a/src/components/navigationBar/NavigationBar.css b/src/components/navigationBar/NavigationBar.css index d4f2845ca..e43bbd72d 100644 --- a/src/components/navigationBar/NavigationBar.css +++ b/src/components/navigationBar/NavigationBar.css @@ -12,33 +12,37 @@ border-bottom: 1px solid #dfdfdf; } -.navbarLeft { +.navbar-left { display: flex; align-items: center; } -.navbarHomeLogo { +.navbar-home-logo { width: 153px; } -.navbarMenu { +.navbar-menu { margin: 0 15px; text-decoration: none; } -.navbarMenu ol { +.navbar-menu ol { list-style-type: none; padding-left: 0; } -.navbarMenuItem { +.navbar-menu-item { display: inline-block; margin: 10px; font-size: 18px; font-weight: bold; } -.navbarMenuItem.active { +.navbar-menu-item.active { + color: var(--blue); +} + +.nav-bar-menu-link.active { color: var(--blue); } diff --git a/src/components/navigationBar/NavigationBar.jsx b/src/components/navigationBar/NavigationBar.jsx index ccbd95479..c45fe3d5f 100644 --- a/src/components/navigationBar/NavigationBar.jsx +++ b/src/components/navigationBar/NavigationBar.jsx @@ -1,29 +1,46 @@ -import { useEffect, useState } from 'react'; +import { NavLink } from 'react-router-dom'; import imgPandaMarketLogo from '../../assets/images/logo/panda-market-logo.png'; import './NavigationBar.css'; function NavigationBar() { return (
-
+
판다마켓 홈 -