diff --git a/src/App.jsx b/src/App.jsx index 6331f0d8..d35b2acd 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -7,6 +7,7 @@ import FreeBoard from './components/pages/FreeBoard'; import Items from './components/pages/Items'; import { Route, Routes } from "react-router"; import AddItem from './components/pages/AddItem'; +import ItemDetail from './components/pages/itemDetail/itemDetail'; function App() { @@ -20,6 +21,7 @@ function App() { } /> } /> } /> + } /> ) diff --git a/src/api/Api.js b/src/api/Api.js index 29103ef6..54e1d2fd 100644 --- a/src/api/Api.js +++ b/src/api/Api.js @@ -8,3 +8,26 @@ export async function getItems({ page=1, pageSize=100, orderBy="recent"}) { return body; } +//productId 받아오는 api +export async function getItemId(productsId) { + const response = await fetch(`https://panda-market-api.vercel.app/products/${productsId}`) + + if (!response.ok) { + throw new Error(`error 상태 ${response.status}`) + } + + const body = await response.json(); + return body +} + +export async function getProductComments(productId, limit=10) { + const response = await fetch(`https://panda-market-api.vercel.app/products/${productId}/comments?limit=${limit}`) + + if (!response.ok) { + + throw new Error(`error 상태 ${response.status}`) + } + const body = await response.json(); + return body.list; +} + diff --git a/src/assets/backIcon.png b/src/assets/backIcon.png new file mode 100644 index 00000000..706c218a Binary files /dev/null and b/src/assets/backIcon.png differ diff --git a/src/assets/kebabIcon.png b/src/assets/kebabIcon.png new file mode 100644 index 00000000..d4676503 Binary files /dev/null and b/src/assets/kebabIcon.png differ diff --git a/src/assets/noCommentImg.png b/src/assets/noCommentImg.png new file mode 100644 index 00000000..0ef0aaff Binary files /dev/null and b/src/assets/noCommentImg.png differ diff --git a/src/components/AllItems.jsx b/src/components/AllItems.jsx index 91a3d7d3..6288d7b1 100644 --- a/src/components/AllItems.jsx +++ b/src/components/AllItems.jsx @@ -3,6 +3,7 @@ import { header, title, itemGrid, + itemLinkStyle, pagenation } from "./AllitemsStyle"; import ItemList from "./ItemList"; @@ -21,7 +22,6 @@ const AllItems = ({ items, onChangeOrder }) => { 상품 등록하기 - {/* */}
- + setItemPrice(e.target.value)}>
- + img { + width: 486px; + aspect-ratio: 1/1; + border-radius: 16px; + object-fit: cover; + + @media(max-width: 768px) { + width: 340px; + } + + @media(max-width: 425px) { + width: 100%; + } + } +` + +const itemDetail = css` + display: flex; + flex-direction: column; + flex: 1; + gap: 24px; +` + +const itemHeader = css` + border-bottom: 1px solid var(--gray-200); + padding-bottom: 16px; + color: var(--gray-800); + + + > h2 { + font-size: 40px; + } +` + +const titleHeader = css` + font-size: 24px; + font-weight: 600; + display: flex; + margin-bottom: 16px; + justify-content: space-between; +` + +const editIconStyle = css` + width: 24px; + height: 24px; + cursor: pointer; +` + +const itemDescription = css` + display: flex; + flex-direction: column; + gap: 16px; + font-weight: 600; + color: var(--gray-600); + font-size: 16px; + + > h3 { + font-weight: 600; + font-size: 16px; + } +` + +const itemTag = css` + margin-bottom: 62px; + font-weight: 600; + color: var(--gray-600); + + > h3 { + font-weight: 600; + font-size: 16px; + } + + > span { + padding: 6px; + } +` + +const tagWrapper = css` + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 16px; +` + +const tagBox = css` + display: inline-flex; + padding: 6px 16px; + background: var(--gray-100); + border-radius: 26px; + border: none; + + font-weight: 600; + color: var(--gray-600); + + > span { + color: var(--gray-800); + font-weight: 400; + } + + > h3 { + font-weight: 600; + } +` + +const itemUserInfo= css` + display: flex; + align-items: center; + margin-top: 24px; +` + +const userImg = css` + width: 40px; + height: 40px; + margin-right: 16px; + + > img { + width: 40px; + height: 40px; + } +` + +const userText = css` + font-weight: 500; + color: var(--gray-600); + font-size: 14px; + flex: 1; + + > p { + margin-top: 6px; + font-weight: 400; + color: var(--gray-400); + } +` + +const likeSection = css ` + // display: inline-flex; + border-radius: 35px; + border: 1px solid var(--gray-200); + padding: 4px 12px; + font-size: 16px; + + > button { + background: transparent; + border: none; + } +` + +const commentSection = css ` + margin: 16px; + +` + +const makeComment = css` + display: flex; + flex-direction: column; + + > textarea { + height: 104px; + border: none; + background: var(--gray-100); + border-radius: 12px; + margin-top: 10px; + margin-bottom: 16px; + padding: 16px; + resize: none; + } +` + +const commentButtonStyle = css` + width: 74px; + height: 42px; + border-radius: 8px; + border: none; + background: var(--gray-400); + align-self: flex-end; + color: white; + cursor: pointer; +` + +const activeButtonStyle = css` + background: var(--blue) +` + +const commentList = css` + list-style: none; + padding: 0; + margin: 0; + gap: 24px; +` + +const commentListStyle =css` + display: flex; + flex-direction: column; + margin-top: 24px; + padding-bottom: 12px; + border-bottom: 1px solid var(--gray-200); +` + +const commentHeader = css` + display: flex; + justify-content: space-between; +` + +const backToListButtonStyle = css` + background: var(--blue); + height: 48px; + color: white; + border: none; + border-radius: 40px; + padding: 12px 40px; + display: flex; + justify-content: center; + align-items: center; + margin: 24px auto; + cursor: pointer; + gap: 8px; + + + > img { + width: 24px; + height: 24px; + } +` + +const linkStyle = css` + text-decoration: none; +` + +const noCommentImgStyles = css` + margin: 10px auto 48px; + display: flex; + justify-content: center; + width: 196px; +` + +const ItemDetail = () => { + + const {productId} = useParams(); + const [item, setItem] = useState(null); + const [comments, setComments] = useState([]); + const [commentTextarea, setCommentTextarea] = useState(""); + const isButtonActive = commentTextarea.trim().length > 0; + + const handleChange = (e) => { + setCommentTextarea(e.target.value); + } + + useEffect(()=> { + async function fetchData() { + try { + const data = await getItemId(productId); + setItem(data); + } catch (err) { + console.error(err); + } + } + + fetchData(); + }, [productId]) + + + useEffect(()=> { + async function fetchData() { + try { + const data = await getProductComments(Number(productId)); + setComments(data); + } catch (error) { + console.error("커멘트 불러오기:", error); + } + } + + if (productId) { + fetchData(); + } + + }, [productId]); + + if (!item) return
로딩 중...
; + + return( +
+
+
+ { + e.target.onerror = null; + e.target.src = defaultImage; + }} + /> +
+
+
+
+

{item.name}

+ +
+

{item.price}원

+
+
+

상품소개

+

{item.description}

+
+
+

상품 태그

+
+ {item.tags.map((tag)=> { + return( +
+ #{tag} +
+ ) + })} +
+
+
+
+ +
+
+

{item.ownerNickname}

+

{new Date(item.createdAt).toISOString().slice(0, 10)}

+
+
+ + {item.favoriteCount} +
+
+
+ +
+ +
+
+ +