diff --git a/src/App.js b/src/App.js index 6e8f1bb04..07ec0b302 100644 --- a/src/App.js +++ b/src/App.js @@ -3,8 +3,9 @@ import Header from "./components/layout/Header"; import BestProducts from "./components/BestProducts/BestProducts"; import AllProducts from "./components/AllProducts/AllProducts"; import { BrowserRouter, Route, Routes } from "react-router-dom"; -import AddItem from "./pages/AddItem"; +import AddItem from "./pages/additem"; import "./styles/global.css"; +import ItemPage from "./pages/ItemPage/ItemPage"; function MainContent() { return ( @@ -21,8 +22,11 @@ function App() {
+ + } /> + } /> + } /> - } /> } />
diff --git a/src/api.js b/src/api.js index 65ead3ebf..a3cb0b55b 100644 --- a/src/api.js +++ b/src/api.js @@ -6,6 +6,42 @@ export async function getData({ const response = await fetch( `https://panda-market-api.vercel.app/products?page=${page}&pageSize=${pageSize}&orderBy=${orderBy}` ); - const body = response.json(); + const body = await response.json(); return body; } + +export async function getDataById(productId) { + const response = await fetch( + `https://panda-market-api.vercel.app/products/${productId}` + ); + const body = await response.json(); + return body; +} + +export async function getUserData({ productId, limit }) { + const response = await fetch( + `https://panda-market-api.vercel.app/products/${productId}/comments?limit=${limit}` + ); + const body = await response.json(); + return body; +} + +export async function postCommentData({ productId, editContent }) { + const token = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTksInNjb3BlIjoiYWNjZXNzIiwiaWF0IjoxNzM3Mjc3NTIzLCJleHAiOjE3MzcyNzkzMjMsImlzcyI6InNwLXBhbmRhLW1hcmtldCJ9.PE7HgmQtdB0J1kQoYk4VieZfBs0CZFwedo2ttRBAHWY"; + + const response = await fetch( + `https://panda-market-api.vercel.app/products/${productId}/comments`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ + content: editContent, + }), + } + ); + return response; +} diff --git a/src/assets/images/ickebab.png b/src/assets/images/ickebab.png new file mode 100644 index 000000000..7f70cebd9 Binary files /dev/null and b/src/assets/images/ickebab.png differ diff --git a/src/assets/images/nocomment.png b/src/assets/images/nocomment.png new file mode 100644 index 000000000..544bab759 Binary files /dev/null and b/src/assets/images/nocomment.png differ diff --git a/src/assets/images/return.png b/src/assets/images/return.png new file mode 100644 index 000000000..d9fbc003b Binary files /dev/null and b/src/assets/images/return.png differ diff --git a/src/components/AllProducts/AllProducts.jsx b/src/components/AllProducts/AllProducts.jsx index 7c2a002ab..29b8067a3 100644 --- a/src/components/AllProducts/AllProducts.jsx +++ b/src/components/AllProducts/AllProducts.jsx @@ -5,6 +5,7 @@ import SearchForm from "../SearchForm/SearchForm"; import Pagination from "../Pagination/Pagination"; import "./AllProducts.css"; import useDevice from "../../hooks/useDevice"; +import { Link } from "react-router-dom"; function AllProducts() { const [allItemList, setAllItemList] = useState([]); @@ -58,6 +59,7 @@ function AllProducts() { {allItemList.map((item) => (
  • {" "} { + const fetchUsers = async () => { + try { + const data = await getUserData({ productId: productSlug, limit: 3 }); + console.log(data.list); + if (data.list && data.list.length > 0) { + setUsers(data.list); + setisValidUsers(true); + } else { + setisValidUsers(false); + } + } catch (error) { + console.error("Error!", error); + } + }; + fetchUsers(); + }, []); + + //2024.01.02 형식으로 렌더링되게. + const formatDate = (dateString) => { + const date = new Date(dateString); + const year = date.getFullYear(); + // getMonth()는 0부터 시작하므로 +1을 해주고, padStart로 2자리 맞추기 + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); + + return `${year}.${month}.${day}`; + }; + + //..시간 전으로 렌더링되게. + function getTimeDifference(createdAt, updatedAt) { + const created = new Date(createdAt); + const updated = new Date(updatedAt); + + // 밀리초 단위의 차이를 시간으로 변환 + const diffInHours = Math.floor((updated - created) / (1000 * 60 * 60)); + + if (diffInHours === 0) { + // 1시간 미만인 경우 분 단위로 표시 + const diffInMinutes = Math.floor((updated - created) / (1000 * 60)); + return `${diffInMinutes}분 전`; + } else if (diffInHours < 24) { + // 24시간 미만인 경우 + return `${diffInHours}시간 전`; + } else { + // 24시간 이상인 경우 일 단위로 표시 + const diffInDays = Math.floor(diffInHours / 24); + return `${diffInDays}일 전`; + } + } + + //수정하기 버튼 클릭했을 때 실행되어야 할 함수 + + const handleEditClick = (userId, content) => { + setEditingId(userId); + setEditContent(content); + }; + + // 취소하기 버튼 클릭했을 때 실행되어야 할 함수 + + const handleCancelClick = () => { + setEditingId(null); + setEditContent(""); + }; + + // 수정완료 버튼 클릭했을 때 실행되어야 할 함수 + + const handleSaveEdit = async (userId) => { + try { + const response = await postCommentData({ + productId: productSlug, + editContent: editContent, + }); + + if (!response.ok) { + throw new Error("failed to update comment"); + } + // 성공시 + + const updateUsers = users.map((user) => { + if (user.id === userId) { + return { ...user, content: editContent }; + } + return user; + }); + setUsers(updateUsers); + setEditingId(null); + setEditContent(""); + } catch (error) { + console.error("error!", error); + } + }; + + return ( +
    +
    +
    + 상품이미지 +
    +
    +

    {name}

    +

    + {price && price.toLocaleString()}원 +

    +
    +
    +

    상품 소개

    +

    + {description} +

    +
    + +
    +

    상품 태그

    +
    + {tags && + tags.map((tag, index) => ( +
    + #{tag} +
    + ))} +
    +
    + +
    +
    + 유저이미지 +
    +
    +

    {ownerNickname}

    +

    {formatDate(createdAt)}

    +
    +
    + 하트이미지 + + {likeCount && likeCount.toLocaleString()} + +
    +
    +
    +
    + +
    +
    +

    문의하기

    + setAskContent(e.target.value)} + type="text" + placeholder="개인정보를 공유 및 요청하거나, 명예 훼손, 무단 광고, 불법 정보 유포시 모니터링 후 삭제될 수 있으며, 이에 대한 민형사상 책임은 게시자에게 있습니다." + className="ask-input" + /> +
    + +
    +
    +
    + {users && isValidUsers ? ( + users.map((user, index) => ( +
    +
    + {editingId === user.id ? ( +
    + setEditContent(e.target.value)} + className="edit-comment-input" + /> +
    + + +
    +
    + ) : ( + <> +
    {user.content}
    + 케밥이미지 handleEditClick(user.id, user.content)} + /> + + )} +
    +
    +
    + 유저이미지 +
    +
    +
    + {user.writer.nickname} +
    +
    + {getTimeDifference(user.createdAt, user.updatedAt)} +
    +
    +
    +
    + )) + ) : ( +
    + 댓글없을 때의 이미지 +
    + )} +
    +
    + +
    + +
    + +
    + ); +} + +export default SpecificProduct; diff --git a/src/components/common/product.jsx b/src/components/common/product.jsx index 45e16093d..8fc97e4cb 100644 --- a/src/components/common/product.jsx +++ b/src/components/common/product.jsx @@ -1,27 +1,41 @@ import "./product.css"; import heartImage from "../../assets/images/heart.png"; import useDevice from "../../hooks/useDevice"; +import { Link } from "react-router-dom"; -function ProductItem({ imageUrl, name, price, likeCount, size }) { +function ProductItem({ id, imageUrl, name, price, likeCount, size }) { return (
    -
    - 베스트아이템이미지 -
    + > +
    + 베스트아이템이미지 +
    +
    +

    {name}

    {price.toLocaleString()}원

    diff --git a/src/components/layout/Header.css b/src/components/layout/Header.css index d2ec14dc6..6f1be72fe 100644 --- a/src/components/layout/Header.css +++ b/src/components/layout/Header.css @@ -6,7 +6,8 @@ align-items: center; justify-content: space-between; height: 70px; - margin-left: 200px; + padding-left: 200px; + border-bottom: 1px solid #dfdfdf; } .nav-container { display: flex; @@ -53,7 +54,9 @@ align-items: center; justify-content: space-between; height: 70px; - margin-left: 24px; + padding-left: 24px; + + border-bottom: 1px solid #dfdfdf; } .nav-container { display: flex; @@ -100,7 +103,8 @@ align-items: center; justify-content: space-between; height: 70px; - margin-left: 24px; + padding-left: 24px; + border-bottom: 1px solid #dfdfdf; } .nav-container { display: flex; diff --git a/src/pages/ItemPage/ItemPage.css b/src/pages/ItemPage/ItemPage.css new file mode 100644 index 000000000..e69de29bb diff --git a/src/pages/ItemPage/ItemPage.jsx b/src/pages/ItemPage/ItemPage.jsx new file mode 100644 index 000000000..b0a288f70 --- /dev/null +++ b/src/pages/ItemPage/ItemPage.jsx @@ -0,0 +1,43 @@ +import { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import { getDataById } from "../../api"; +import SpecificProduct from "../../components/common/SpecificProduct"; + +function ItemPage() { + const { productSlug } = useParams(); + const [item, setItem] = useState(); + const [size, setSize] = useState(486); + + const fetchItems = async () => { + try { + const data = await getDataById(productSlug); + setItem(data); + } catch (error) { + console.error("Error!"); + } + }; + + useEffect(() => { + fetchItems(); + }, [productSlug]); + + return ( + <> + {item && ( + + )} + + ); +} + +export default ItemPage;