diff --git a/.github/delete-merged-branch-config.yml b/.github/delete-merged-branch-config.yml
deleted file mode 100644
index 2a6d27bef..000000000
--- a/.github/delete-merged-branch-config.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: delete branch on close pr
-
-on:
- pull_request:
- types: [closed]
-
-permissions:
- pull-requests: write
-
-jobs:
- delete-branch:
- runs-on: ubuntu-latest
- steps:
- - name: delete branch
- uses: SvanBoxel/delete-merged-branch@main
diff --git a/src/Main.js b/src/Main.js
index f0f815d2e..93cad085c 100644
--- a/src/Main.js
+++ b/src/Main.js
@@ -3,6 +3,7 @@ import App from "./components/App";
import Items from './pages/Items';
import AddItems from './pages/AddItem';
import NotFound from './pages/NotFound';
+import Products from './pages/Products';
function Main() {
@@ -11,8 +12,12 @@ function Main() {
} >
- }>
+
+ }>
+ }>
+
}>
+
}>
diff --git a/src/api.js b/src/api.js
index 7b36c0cf1..f5fa63ef9 100644
--- a/src/api.js
+++ b/src/api.js
@@ -1,16 +1,28 @@
const BASE_URL = 'https://panda-market-api.vercel.app';
-export async function getProducts(order = 'recent') {
+export async function getProducts({ order = 'recent', page = 1, pageSize }) {
const query = `orderBy=${order}`;
- const response = await fetch(`${BASE_URL}/products?${query}`);
+ const response = await fetch(`${BASE_URL}/products?${query}&page=${page}&pageSize=${pageSize}`);
const body = await response.json();
return body;
+}
+export async function getProduct(productSlug) {
+ const response = await fetch(`${BASE_URL}/products/${productSlug}`);
+ const body = await response.json();
+ return body;
}
-export async function createProducts(formData) {
+export async function getComment(productSlug) {
+ const response = await fetch(`${BASE_URL}/products/${productSlug}/comments?limit=10`)
+ const body = await response.json();
+ return body;
+
+}
+
+export async function createProducts(formData) {
const response = await fetch(`${BASE_URL}/products?`,
{
@@ -21,5 +33,4 @@ export async function createProducts(formData) {
const body = await response.json();
return body;
-}
-
+}
\ No newline at end of file
diff --git a/src/components/App.js b/src/components/App.js
index 9036050e7..119747d51 100644
--- a/src/components/App.js
+++ b/src/components/App.js
@@ -1,5 +1,5 @@
-import Topbar from './Topbar';
+import Topbar from './Topbar/Topbar'
import './App.css';
import { Outlet } from 'react-router-dom';
diff --git a/src/components/BestProductList.js b/src/components/BestProductList.js
deleted file mode 100644
index 4e47e36be..000000000
--- a/src/components/BestProductList.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import './BestProductList.css';
-
-function BestProductListItem({ item }) {
-
- return (
-
- {displayedItems.map((item) => {
- return (
-
-
- );
- })}
-
- )
-}
-
-export default BestProductList;
\ No newline at end of file
diff --git a/src/components/BestProductList.css b/src/components/BestProductList/BestProductList.css
similarity index 88%
rename from src/components/BestProductList.css
rename to src/components/BestProductList/BestProductList.css
index 6a7768ac8..ed71340ac 100644
--- a/src/components/BestProductList.css
+++ b/src/components/BestProductList/BestProductList.css
@@ -15,6 +15,8 @@
display: flex;
flex-direction: column;
padding: 24px 0;
+ border: 0;
+ background-color: transparent;
}
.BestProductListItem p,
diff --git a/src/components/BestProductList/BestProductList.js b/src/components/BestProductList/BestProductList.js
new file mode 100644
index 000000000..2c642174b
--- /dev/null
+++ b/src/components/BestProductList/BestProductList.js
@@ -0,0 +1,52 @@
+import { useEffect, useState } from 'react';
+import { getProducts } from '../../api';
+import { Link, useNavigate } from 'react-router-dom';
+import './BestProductList.css';
+
+function BestProductListItem({ item }) {
+ const navigate = useNavigate();
+ const handleAddItemClick = () => {
+ navigate(`/items/${item.id}`);
+ }
+
+ return (
+
+ {displayedItems.map((item) => {
+ return (
+
+
+ );
+ })}
+
+ )
+}
+
+export default BestProductList;
\ No newline at end of file
diff --git a/src/components/Button.css b/src/components/Button/Button.css
similarity index 76%
rename from src/components/Button.css
rename to src/components/Button/Button.css
index 795ab07ea..9bf3517a7 100644
--- a/src/components/Button.css
+++ b/src/components/Button/Button.css
@@ -22,3 +22,13 @@
font-size: 18px;
font-weight: 700;
}
+
+.disable {
+ background-color: #9ca3af;
+ color: #ffffff;
+}
+
+.inquiry {
+ width: 74px;
+ align-self: flex-end;
+}
diff --git a/src/components/Button.js b/src/components/Button/Button.js
similarity index 57%
rename from src/components/Button.js
rename to src/components/Button/Button.js
index f30d36f5e..b47740fe0 100644
--- a/src/components/Button.js
+++ b/src/components/Button/Button.js
@@ -3,8 +3,8 @@ import './Button.css';
-function Button({ children, onClick, select = '' }) {
- const classNames = `Button ${select}`;
+function Button({ children, onClick, select = '', width = '' }) {
+ const classNames = `Button ${select} ${width}`;
return (
diff --git a/src/components/Topbar.css b/src/components/Topbar/Topbar.css
similarity index 100%
rename from src/components/Topbar.css
rename to src/components/Topbar/Topbar.css
diff --git a/src/components/Topbar.js b/src/components/Topbar/Topbar.js
similarity index 86%
rename from src/components/Topbar.js
rename to src/components/Topbar/Topbar.js
index c66e1da04..c6bdeca5d 100644
--- a/src/components/Topbar.js
+++ b/src/components/Topbar/Topbar.js
@@ -1,6 +1,6 @@
-import pandaLogo from '../img/logo.png';
+import pandaLogo from '../../img/logo.png';
import './Topbar.css';
-import Button from './Button'
+import Button from '../Button/Button';
import { Link } from 'react-router-dom';
function Topbar() {
diff --git a/src/components/pageNation/PageNation.css b/src/components/pageNation/PageNation.css
new file mode 100644
index 000000000..524ec334b
--- /dev/null
+++ b/src/components/pageNation/PageNation.css
@@ -0,0 +1,12 @@
+.pageButton {
+ width: 40px;
+ height: 40px;
+ border-radius: 40px;
+ background-color: #ffffff;
+ border: #e5e7eb 1px solid;
+}
+
+.active {
+ background-color: #3692ff;
+ color: #ffffff;
+}
diff --git a/src/components/pageNation/PageNation.js b/src/components/pageNation/PageNation.js
new file mode 100644
index 000000000..63555e2ff
--- /dev/null
+++ b/src/components/pageNation/PageNation.js
@@ -0,0 +1,22 @@
+import './PageNation.css';
+
+function PageNation({ totalPages, currentPage, handlePageChange }) {
+
+ return (
+
+
+ {Array.from({ length: totalPages }, (_, i) => (
+
+ ))}
+
+
+ )
+}
+
+export default PageNation;
\ No newline at end of file
diff --git a/src/img/Img_inquiry_empty.png b/src/img/Img_inquiry_empty.png
new file mode 100644
index 000000000..20f0821bb
Binary files /dev/null and b/src/img/Img_inquiry_empty.png differ
diff --git a/src/img/ic_back.png b/src/img/ic_back.png
new file mode 100644
index 000000000..69ce99c35
Binary files /dev/null and b/src/img/ic_back.png differ
diff --git a/src/img/ic_heart.png b/src/img/ic_heart.png
new file mode 100644
index 000000000..587b90cd9
Binary files /dev/null and b/src/img/ic_heart.png differ
diff --git a/src/pages/AddItem.js b/src/pages/AddItem.js
index 4b2a62dfe..2b28708ed 100644
--- a/src/pages/AddItem.js
+++ b/src/pages/AddItem.js
@@ -1,6 +1,6 @@
import './Additem.css';
import { useEffect, useState } from "react";
-import FileInput from "../components/FileInput";
+import FileInput from '../components/FileInput/FileInput';
function AddItem() {
@@ -12,15 +12,15 @@ function AddItem() {
imgFile: null,
});
- const [isButtonDisabled, SetisButtonDisabled] = useState(true);
+ const [isButtonDisabled, setIsButtonDisabled] = useState(true);
useEffect(() => {
const allInput = values.introduction && values.price && values.productName && values.tag;
if (allInput) {
- SetisButtonDisabled(false);
+ setIsButtonDisabled(false);
}
else {
- SetisButtonDisabled(true);
+ setIsButtonDisabled(true);
}
}, [values])
@@ -39,7 +39,6 @@ function AddItem() {
const handleSubmit = (e) => {
e.preventDefault();
- console.log(values);
}
diff --git a/src/pages/Items.css b/src/pages/Items.css
index 506124967..40561b91b 100644
--- a/src/pages/Items.css
+++ b/src/pages/Items.css
@@ -23,3 +23,9 @@
padding: 9px 20px 9px 16px;
outline: none;
}
+
+.pagination {
+ display: flex;
+ justify-content: center;
+ margin-top: 20px;
+}
diff --git a/src/pages/Items.js b/src/pages/Items.js
index 1ad46922a..2e79b458f 100644
--- a/src/pages/Items.js
+++ b/src/pages/Items.js
@@ -1,43 +1,59 @@
-import ProductList from '../components/ProductList';
-import BestProductList from '../components/BestProductList';
-import Button from '../components/Button';
+import ProductList from '../components/ProductList/ProductList';
+import BestProductList from '../components/BestProductList/BestProductList';
+import Button from '../components/Button/Button';
import { useEffect, useState } from 'react';
import { getProducts } from '../api';
import './Items.css';
import { Link, useNavigate } from 'react-router-dom';
+import PageNation from '../components/pageNation/PageNation';
function Items() {
+ const navigate = useNavigate();
const [order, setOrder] = useState('recent');
const [item, setItem] = useState([]);
+ const [totalCount, setTotalCount] = useState(1);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [pageSize, setPageSize] = useState(10);
+ const [totalPages, setTotalPages] = useState(0);
- const SortedItems = item.sort((a, b) => b[order] - a[order]);
+ useEffect(() => {
+ handleLoadMore({ order, page: currentPage, pageSize });
+ }, [order, currentPage])
- const bestItems = item.sort((a, b) => b['favorite'] - a['favorite']);
+ useEffect(() => {
+ setTotalPages(Math.ceil(totalCount / pageSize));
+ //Math.ceil(전체 컨텐츠 개수 / 한 페이지에 보여주고자 하는 컨텐츠의 개수)
+ }, [item])
- const handleFavorite = () => setOrder('favorite');
+ const SortedItems = item.sort((a, b) => b[order] - a[order]);
+ const handleFavorite = () => setOrder('favorite');
const handleRecent = () => setOrder('recent');
- const navigate = useNavigate();
+ const handleLoadMore = async () => {
+ await handleLoad({ order, page: currentPage, pageSize });
+ }
const handleLoad = async (orderQuery) => {
- const { list } = await getProducts(orderQuery);
+ const { list, totalCount } = await getProducts(orderQuery);
setItem(list);
+ setTotalCount(totalCount);
}
- useEffect(() => {
- handleLoad(order);
- }, [order])
const handleAddItemClick = () => {
navigate('/additem');
}
+ const handlePageChange = (page) => {
+ setCurrentPage(page);
+ }
+
return (
베스트 상품
-
+
판매 중인 상품
@@ -54,6 +70,7 @@ function Items() {
+
)
diff --git a/src/pages/Products.css b/src/pages/Products.css
new file mode 100644
index 000000000..9851bf4e0
--- /dev/null
+++ b/src/pages/Products.css
@@ -0,0 +1,124 @@
+.itemWrapper {
+ display: flex;
+ gap: 24px;
+ padding-bottom: 30px;
+ border-bottom: 1px solid #e5e7eb;
+}
+
+.itemImg {
+ width: 486px;
+ height: 486px;
+ border-radius: 16px;
+}
+
+.itemLetter {
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+ width: 100%;
+}
+
+.itemLetterTop {
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #e5e7eb;
+}
+
+.itemLetterBottom {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+}
+
+.itemName {
+ font-size: 24px;
+ font-weight: 600;
+ line-height: 28.64px;
+}
+
+.itemTitle {
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 16.71px;
+ color: #4b5563;
+}
+
+.itemPrice {
+ font-size: 40px;
+ font-weight: 600;
+ line-height: 47.73px;
+}
+
+.itemitemDescription {
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 16.71px;
+ color: #1f2937;
+}
+
+.itemTags {
+ display: flex;
+ gap: 10px;
+ padding: 10px 0;
+}
+
+.itemTag {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 6px 16px;
+ height: 36px;
+ border-radius: 26px;
+ background-color: #f3f4f6;
+}
+
+.itemFavorite {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+ padding: 6px 16px;
+ height: 36px;
+ width: 87px;
+ border-radius: 26px;
+ border: 1px solid #e5e7eb;
+ margin: auto 0 0;
+}
+
+.inquiryWrapper {
+ display: flex;
+ width: auto;
+ flex-direction: column;
+ gap: 16px;
+ margin: 24px 0;
+}
+
+.inquiryInput {
+ width: 100%;
+ height: 104px;
+ border-radius: 12px;
+ background-color: #f3f4f6;
+ border: none;
+ color: #9ca3af;
+ font-size: 16px;
+ font-weight: 400;
+ line-height: 24px;
+ padding: 0 16px;
+ outline: none;
+}
+
+.backButton {
+ display: flex;
+ margin: 40px auto;
+ gap: 10px;
+ background-color: #3692ff;
+ color: #ffffff;
+ border-radius: 40px;
+ padding: 12px 71px;
+ font-size: 18px;
+ font-weight: 600;
+ text-decoration: none;
+ border: none;
+}
diff --git a/src/pages/Products.js b/src/pages/Products.js
new file mode 100644
index 000000000..f906ef42f
--- /dev/null
+++ b/src/pages/Products.js
@@ -0,0 +1,77 @@
+import { useParams } from "react-router-dom";
+import { useEffect, useState } from "react";
+import { getProduct } from '../api';
+import './Products.css';
+import Button from "../components/Button/Button";
+import { useNavigate } from 'react-router-dom';
+import favoriteImg from '../img/ic_heart.png'
+import backImg from '../img/ic_back.png'
+import CommentList from "../components/CommentList/CommentList";
+
+
+function Products() {
+ const navigate = useNavigate();
+ const { productSlug } = useParams();
+ const [item, setItem] = useState([]);
+ const [inquiry, setInquiry] = useState('');
+
+ useEffect(() => {
+ handleLoad(productSlug);
+ }, [productSlug]);
+
+
+ const handleLoad = async (productSlug) => {
+ const list = await getProduct(productSlug);
+ setItem(list);
+ }
+
+ const handleBackClick = () => {
+ navigate('/items');
+ }
+
+ const handleInquriyChange = (e) => {
+ setInquiry(e.target.value);
+ }
+
+ return (
+
+
![{item.name}]({item.images})
+
+
+ {item.name}
+ {item.price}원
+
+
+
상품소개
+
{item.description}
+
+
상품태그
+
+ {item.tags && item.tags.map((tag, index) => (
+
#{tag}
+ ))}
+
+
+
![]({favoriteImg})
+
{item.favoriteCount}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
)
+}
+
+export default Products;
\ No newline at end of file