diff --git a/public/favicon.ico b/public/favicon.ico
deleted file mode 100644
index a11777cc4..000000000
Binary files a/public/favicon.ico and /dev/null differ
diff --git a/public/logo192.png b/public/logo192.png
deleted file mode 100644
index fc44b0a37..000000000
Binary files a/public/logo192.png and /dev/null differ
diff --git a/public/logo512.png b/public/logo512.png
deleted file mode 100644
index a4e47a654..000000000
Binary files a/public/logo512.png and /dev/null differ
diff --git a/src/Api.js b/src/Api.js
deleted file mode 100644
index 3523179e0..000000000
--- a/src/Api.js
+++ /dev/null
@@ -1,13 +0,0 @@
-//product item list api
-export async function getItems({
- pageSize = 12,
- orderBy = "recent",
- page = "1",
-}) {
- const query = `pageSize=${pageSize}&orderBy=${orderBy}&page=${page}`;
- const response = await fetch(
- `https://panda-market-api.vercel.app/products?${query}`
- );
- const body = await response.json();
- return body;
-}
diff --git a/src/api/Api.js b/src/api/Api.js
new file mode 100644
index 000000000..f5e503c8b
--- /dev/null
+++ b/src/api/Api.js
@@ -0,0 +1,26 @@
+//product item list api
+const BASEURL = "https://panda-market-api.vercel.app";
+export async function getItems({
+ pageSize = 12,
+ orderBy = "recent",
+ page = "1",
+}) {
+ const query = `pageSize=${pageSize}&orderBy=${orderBy}&page=${page}`;
+ const response = await fetch(`${BASEURL}/products?${query}`);
+ const body = await response.json();
+ return body;
+}
+
+//product item detail api
+export async function getItemDetail({ id }) {
+ const response = await fetch(`${BASEURL}/products/${id}`);
+ const body = await response.json();
+ return body;
+}
+
+//product item comment api
+export async function getItemDetailComment({ id }) {
+ const response = await fetch(`${BASEURL}/products/${id}/comments?limit=30`);
+ const body = await response.json();
+ return body;
+}
diff --git a/src/component/Header.js b/src/component/Header.js
index a710795ea..57de4c36a 100644
--- a/src/component/Header.js
+++ b/src/component/Header.js
@@ -2,7 +2,7 @@ import { Link, NavLink } from "react-router-dom";
import LogoImg from "../images/logo.png";
import "../css/common.css";
-function getLink({ isActive }) {
+function getLinkStyle({ isActive }) {
return {
color: isActive ? "#3182f6" : undefined,
};
@@ -17,10 +17,10 @@ function Header() {
diff --git a/src/component/Product/BasicProducts.js b/src/component/Product/BasicProducts.js
index b2247f37b..6ece26d33 100644
--- a/src/component/Product/BasicProducts.js
+++ b/src/component/Product/BasicProducts.js
@@ -1,4 +1,4 @@
-import { getItems } from "../../Api.js";
+import { getItems } from "../../api/Api.js";
import Select from "react-select";
import { useState, useEffect, React } from "react";
import ProductList from "./ProductList.js";
@@ -16,25 +16,21 @@ function BasicProducts() {
{ value: "favorite", label: "좋아요순", className: "order-value-favorite" },
];
const customStyles = {
- control: (styles) => ({
- ...styles,
+ control: (provided) => ({
borderRadius: "10px",
border: "1px solid #ddd",
boxShadow: "none",
padding: "5px 6px",
- }),
- option: (styles, { isFocused, isSelected }) => ({
- ...styles,
"&:hover": {
color: "#7db4f4",
},
- color: isSelected ? "#3182f6" : undefined,
- zIndex: 1,
- background: isFocused ? "#fff" : isSelected ? "#fff" : undefined,
}),
- input: (styles) => ({
+ option: (styles, { isFocused, isSelected }) => ({
...styles,
- color: "transparent",
+ color: isSelected ? "#3182f6" : "inherit",
+ boderBottom: "1px solid #ddd",
+ zIndex: 1,
+ background: isFocused ? "#fff" : isSelected ? "#fff" : undefined,
}),
};
const handlePage = (pageNumber) => {
@@ -47,8 +43,7 @@ function BasicProducts() {
};
const productsLoad = async (orderBy, page) => {
- const { list } = await getItems({ orderBy, page });
- const { totalCount } = await getItems({ pageSize: 12 });
+ const { list, totalCount } = await getItems({ orderBy, page });
setTotalCount(totalCount);
setItems(list);
};
@@ -61,7 +56,7 @@ function BasicProducts() {
전체 상품
-
+
diff --git a/src/component/Product/BestProduct.js b/src/component/Product/BestProduct.js
index 38f504d26..c669d6361 100644
--- a/src/component/Product/BestProduct.js
+++ b/src/component/Product/BestProduct.js
@@ -1,6 +1,6 @@
import ProductList from "./ProductList.js";
import { useState, useEffect, React } from "react";
-import { getItems } from "../../Api.js";
+import { getItems } from "../../api/Api.js";
function BestProducts() {
const [items, setItems] = useState([]);
diff --git a/src/component/Product/FileInput.js b/src/component/Product/FileInput.js
index e2ec31088..114277d37 100644
--- a/src/component/Product/FileInput.js
+++ b/src/component/Product/FileInput.js
@@ -52,12 +52,13 @@ function FormInput({ name, value, onChange }) {
return (
-

inputRef.current?.click()} //useRef 사용해서 이미지 클릭시 input 태그가 클릭
- className="img_placeholder"
- />
+
{preview && (
![]()
diff --git a/src/component/Product/ProductDetailComment.js b/src/component/Product/ProductDetailComment.js
new file mode 100644
index 000000000..4a9ab3fbd
--- /dev/null
+++ b/src/component/Product/ProductDetailComment.js
@@ -0,0 +1,54 @@
+import { useState } from "react";
+
+function CommentItem({ item }) {
+ return (
+
+
{item.content}
+
+

+
+
{item.writer.nickname}
+
{item.updatedAt}
+
+
+
+ );
+}
+
+function ProductDetailComment({ comments }) {
+ const [inquiry, setinquiry] = useState("");
+ const handleInputChange = (e) => {
+ setinquiry(e.target.value);
+ };
+ const abledButton = () => {
+ //등록 버튼 활성화
+ if (inquiry) {
+ return true;
+ } else {
+ return false;
+ }
+ };
+ return (
+
+
+
+ {comments.length === 0 ? (
+
+ ) : (
+ comments?.map((item) =>
)
+ )}
+
+
+ );
+}
+
+export default ProductDetailComment;
diff --git a/src/component/Product/ProductList.js b/src/component/Product/ProductList.js
index 0a01a2efb..475b0baa2 100644
--- a/src/component/Product/ProductList.js
+++ b/src/component/Product/ProductList.js
@@ -1,20 +1,22 @@
+import { Link } from "react-router-dom";
import heartIcon from "../../images/Icon.png";
function ProdictListItem({ item }) {
const price = item.price.toLocaleString("ko-KR");
return (
-
-
-

+
+
+
+

+
+
{item.name}
+
{price}원
+
+
{item.favoriteCount}
+
-
{item.name}
-
{price}원
-
- {" "}
-
{item.favoriteCount}
-
-
+
);
}
diff --git a/src/component/Product/Tags.js b/src/component/Product/Tags.js
index a047731f3..747e53ac5 100644
--- a/src/component/Product/Tags.js
+++ b/src/component/Product/Tags.js
@@ -37,7 +37,7 @@ function Tags({ name, value, onChange }) {
/>
{addTags &&
- addTags.map((tag, index) => (
+ addTags?.map((tag, index) => (
-
{tag}
{
+ const [productList, setProductList] = useState([]);
+ const [totalCount, setTotalCount] = useState(0);
+
+ useEffect(() => {
+ const fetchProductList = async () => {
+ try {
+ const { list, totalCount } = await getItems(loadOptions);
+ setProductList(list);
+ setTotalCount(totalCount);
+ } catch (error) {
+ console.error("상품 목록을 불러오는 도중 오류가 발생했습니다:", error);
+ }
+ };
+
+ fetchProductList();
+ }, [loadOptions]);
+
+ return { productList, totalCount };
+};
+
+export default useProductList;
diff --git a/src/css/Product.css b/src/css/Product.css
index 5292cad05..a127a7e2a 100644
--- a/src/css/Product.css
+++ b/src/css/Product.css
@@ -122,7 +122,8 @@
margin-bottom: 20px;
}
-#addItem_btn {
+#addItem_btn,
+.detail-inquiry button {
float: right;
font-size: 16px;
padding: 9px 20px;
@@ -132,7 +133,8 @@
background: var(--blue);
cursor: pointer;
}
-#addItem_btn[disabled] {
+#addItem_btn[disabled],
+.detail-inquiry button[disabled] {
background: var(--gray);
}
.addItem_header h2 {
@@ -148,6 +150,9 @@
.FileInput {
margin-bottom: 20px;
}
+.FileInput label {
+ display: inline-block;
+}
.preview_area {
width: 282px;
height: 282px;
@@ -175,7 +180,8 @@
cursor: pointer;
}
.AddItemForm input,
-.AddItemForm textarea {
+.AddItemForm textarea,
+.detail-inquiry textarea {
width: 100%;
border: none;
background: var(--coolGray);
@@ -183,9 +189,11 @@
padding: 16px 24px;
border-radius: 12px;
margin-bottom: 24px;
+ outline: none;
}
.AddItemForm input::placeholder,
-.AddItemForm textarea::placeholder {
+.AddItemForm textarea::placeholder,
+.detail-inquiry textarea::placeholder {
color: var(--gray);
}
.AddItemForm textarea {
@@ -209,3 +217,120 @@
.InputTags ul li img {
cursor: pointer;
}
+
+/*Detail*/
+.product-detail {
+ display: grid;
+ grid-template-columns: 1fr 2fr;
+ gap: 24px;
+ padding-bottom: 32px;
+ border-bottom: 1px solid #e5e7eb;
+ margin-bottom: 24px;
+}
+.detail-img {
+ border-radius: 16px;
+ width: 100%;
+}
+.detail-desc {
+ position: relative;
+}
+.detail-desc h2 {
+ font-size: 24px;
+ margin-bottom: 16px;
+}
+.detail-desc h3 {
+ font-size: 40px;
+ padding-bottom: 16px;
+ border-bottom: 1px solid #e5e7eb;
+ margin-bottom: 16px;
+ line-height: 1;
+}
+.detail-desc p {
+ margin-bottom: 24px;
+}
+.detail-desc span {
+ display: block;
+ font-size: 14px;
+ color: #4b5563;
+ margin-bottom: 8px;
+}
+.detail-desc ul {
+ display: flex;
+ gap: 8px;
+}
+.detail-desc ul li {
+ background: #f3f4f6;
+ border-radius: 30px;
+ font-size: 16px;
+ padding: 6px 16px;
+}
+.detail-favorite {
+ border: 1px solid #e5e7eb;
+ font-weight: 500;
+ border-radius: 30px;
+ padding: 4px 12px;
+ display: flex;
+ gap: 5px;
+ font-size: 16px;
+ color: #6b7280;
+ align-items: center;
+ width: fit-content;
+ position: absolute;
+ bottom: 0;
+}
+
+/*detail comment*/
+.detail-inquiry {
+ margin-bottom: 80px;
+}
+.detail-inquiry p {
+ font-weight: bold;
+ margin-bottom: 16px;
+}
+.detail-inquiry textarea {
+ resize: none;
+ height: 105px;
+}
+.comment-item {
+ margin-bottom: 24px;
+ border-bottom: 1px solid #e5e7eb;
+ padding-bottom: 24px;
+}
+
+.user-info {
+ display: flex;
+ gap: 8px;
+ margin-top: 24px;
+ align-items: center;
+}
+.user-info img {
+ width: 100%;
+ max-width: 40px;
+}
+.user-name p {
+ font-size: 14px;
+ color: #4b5563;
+}
+.user-name span {
+ font-size: 12px;
+ color: #9ca3af;
+}
+.detail-button a {
+ background: var(--blue);
+ color: #fff;
+ padding: 12px 38.5px;
+ display: flex;
+ border-radius: 30px;
+ gap: 5px;
+ width: fit-content;
+ margin: 0 auto;
+}
+
+.empty-comment {
+ text-align: center;
+ border-top: 1px solid #e5e7eb;
+ border-bottom: 1px solid #e5e7eb;
+ padding: 40px 0;
+ color: #9ca3af;
+ margin-bottom: 40px;
+}
diff --git a/src/css/common.css b/src/css/common.css
index f12009c1b..e63adbeec 100644
--- a/src/css/common.css
+++ b/src/css/common.css
@@ -35,7 +35,7 @@ body {
.container {
width: 100%;
max-width: 1200px;
- margin: 0 auto;
+ margin: 0 auto 60px;
}
.img_responsive {
width: auto;
diff --git a/src/images/.DS_Store b/src/images/.DS_Store
index 832a18c0d..8f2c8460c 100644
Binary files a/src/images/.DS_Store and b/src/images/.DS_Store differ
diff --git a/src/images/backPage.png b/src/images/backPage.png
new file mode 100644
index 000000000..6c5cbec22
Binary files /dev/null and b/src/images/backPage.png differ
diff --git a/src/images/favoriteCount.png b/src/images/favoriteCount.png
new file mode 100644
index 000000000..abf0a75bc
Binary files /dev/null and b/src/images/favoriteCount.png differ
diff --git a/src/images/thumbnail.png b/src/images/thumbnail.png
new file mode 100644
index 000000000..ae27943e2
Binary files /dev/null and b/src/images/thumbnail.png differ
diff --git a/src/page/App.js b/src/page/App.js
index adfa3b2f7..ff1dd2456 100644
--- a/src/page/App.js
+++ b/src/page/App.js
@@ -7,6 +7,7 @@ import NotFound from "./NotFound";
import Join from "../component/member/Join";
import Footer from "../component/Footer";
import HomePage from "./HomePage";
+import ProductDetail from "./ProductDetail";
function App() {
return (
@@ -18,6 +19,7 @@ function App() {
} />
} />
+ } />
} />
} />
diff --git a/src/page/ProductDetail.js b/src/page/ProductDetail.js
new file mode 100644
index 000000000..3c74c17aa
--- /dev/null
+++ b/src/page/ProductDetail.js
@@ -0,0 +1,65 @@
+import { Link, useParams } from "react-router-dom";
+import { getItemDetail, getItemDetailComment } from "../api/Api";
+import { useEffect, useState } from "react";
+import favoriteCount from "./../images/favoriteCount.png";
+import backPage from "./../images/backPage.png";
+import ProductDetailComment from "./../component/Product/ProductDetailComment";
+
+function ProductDetail() {
+ const [view, setView] = useState(null);
+ const [comment, setComment] = useState(null);
+ const { id } = useParams();
+
+ const DetailLoad = async (id) => {
+ const item = await getItemDetail({ id });
+ const { list } = await getItemDetailComment({ id });
+ setComment(list);
+ setView(item);
+ };
+ useEffect(() => {
+ DetailLoad(id);
+ }, [id]);
+
+ if (!view) {
+ return null;
+ }
+
+ if (!comment) {
+ return null;
+ }
+
+ return (
+
+
+

+
+
{view.name}
+
{view.price.toLocaleString("ko-KR")}원
+
+ 상품소개
+ {view.description}
+
+
상품 태그
+
+ {view.tags.map((tag, index) => (
+ - #{tag}
+ ))}
+
+
+

+ {view.favoriteCount}
+
+
+
+
+
+
+
목록으로 돌아가기
+

+
+
+
+ );
+}
+
+export default ProductDetail;