diff --git a/public/images/common/Img_inquiry_empty.png b/public/images/common/Img_inquiry_empty.png new file mode 100644 index 00000000..249083a3 Binary files /dev/null and b/public/images/common/Img_inquiry_empty.png differ diff --git a/public/images/common/ic_back.svg b/public/images/common/ic_back.svg new file mode 100644 index 00000000..e11d6d75 --- /dev/null +++ b/public/images/common/ic_back.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/common/ic_heart_detail.svg b/public/images/common/ic_heart_detail.svg new file mode 100644 index 00000000..1139d16b --- /dev/null +++ b/public/images/common/ic_heart_detail.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/common/ic_kebab.svg b/public/images/common/ic_kebab.svg new file mode 100644 index 00000000..dd7ed7f5 --- /dev/null +++ b/public/images/common/ic_kebab.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/App.css b/src/App.css deleted file mode 100644 index e69de29b..00000000 diff --git a/src/App.jsx b/src/App.jsx index 2556b37d..8e36ad5e 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,33 +1,10 @@ -import './App.css'; -import './styles/reset.css'; -import './styles/layout.css'; -import './styles/common.css'; -import { useState, useReducer, useRef, createContext } from 'react'; -import { Routes, Route } from 'react-router-dom'; -import Login from './pages/Login'; -import Signup from './pages/Signup'; -import Items from './pages/Items'; -import Board from './pages/Board'; -import Notfound from './pages/Notfound'; -import AddItem from './pages/AddItem'; -import Home from './pages/Home'; - -const AuthStateContext = createContext(); -const AuthDispatchContext = createContext(); -export { AuthStateContext, AuthDispatchContext }; +import './styles/index.css'; +import AppRoutes from './routes/AppRoutes'; function App() { return ( <> - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - + ); } diff --git a/src/api/productCommentAPI.js b/src/api/productCommentAPI.js new file mode 100644 index 00000000..2d07aaf8 --- /dev/null +++ b/src/api/productCommentAPI.js @@ -0,0 +1,21 @@ +import { baseAPI } from './axios'; + +export const productCommentAPI = { + // 상품 댓글 조회 + getProductComments: async (productId, limit, cursor) => { + try { + // cursor가 null이면 limit만 전달 + const queryParams = cursor + ? `limit=${limit}&cursor=${cursor}` + : `limit=${limit}`; + + const response = await baseAPI.get( + `/products/${productId}/comments?${queryParams}` + ); + return response.data; + } catch (error) { + throw new Error('댓글을 불러오는데 실패했습니다.'); + } + }, + +}; diff --git a/src/components/Detail/CommentItemMenu.jsx b/src/components/Detail/CommentItemMenu.jsx new file mode 100644 index 00000000..fd6d8d6c --- /dev/null +++ b/src/components/Detail/CommentItemMenu.jsx @@ -0,0 +1,21 @@ +import styles from './styles/CommentItemMenu.module.css'; + +export default function CommentItemMenu({ isOpen, onClick }) { + return ( +
+ + {isOpen && ( + + )} +
+ ); +}; diff --git a/src/components/Detail/DetailBackToListButton.jsx b/src/components/Detail/DetailBackToListButton.jsx new file mode 100644 index 00000000..e848467a --- /dev/null +++ b/src/components/Detail/DetailBackToListButton.jsx @@ -0,0 +1,19 @@ +import { useLocation } from 'react-router-dom'; +import styles from './styles/DetailBackToListButton.module.css'; +import { useNavigate } from 'react-router-dom'; + +export default function DetailBackToListButton() { + const navigate = useNavigate(); + const location = useLocation(); + const searchParams = new URLSearchParams(location.search); + + const handleBackToList = () => { + const prevQuery = location.state?.prevQuery || ''; + navigate(`/items${prevQuery}`); + }; + return ( + + ); +} diff --git a/src/components/Detail/DetailComment.jsx b/src/components/Detail/DetailComment.jsx new file mode 100644 index 00000000..4d4e6849 --- /dev/null +++ b/src/components/Detail/DetailComment.jsx @@ -0,0 +1,43 @@ +import styles from './styles/DetailComment.module.css'; +import { useProductComment } from '@/hooks/useProductComment'; +import { relativeTime } from '@/utils/relativeTimeUtils'; +import DetailProfile from '@/components/Detail/DetailProfile'; +import CommentItemMenu from '@/components/Detail/CommentItemMenu'; +import { useState } from 'react'; + +export default function DetailComment({ productId }) { + const { productComments, loading, error } = useProductComment(productId, 3, null); + const [openMenuId, setOpenMenuId] = useState(null); + + if (loading) return
로딩 중...
; + if (error) return
{error}
; + if (!productComments?.list?.length) return ( +
+ 댓글이 없습니다. +

아직 문의가 없어요

+
+ ); + + const onClickMenu = (commentId) => { + setOpenMenuId(openMenuId === commentId ? null : commentId); + }; + + return ( + + ); +} diff --git a/src/components/Detail/DetailInquiry.jsx b/src/components/Detail/DetailInquiry.jsx new file mode 100644 index 00000000..1a5701fb --- /dev/null +++ b/src/components/Detail/DetailInquiry.jsx @@ -0,0 +1,24 @@ +import styles from './styles/DetailInquiry.module.css'; +import CommonButton from '@/components/Common/CommonButton'; +import { useState } from 'react'; + +export default function DetailInquiry({ label }) { + const [inquiry, setInquiry] = useState(''); + + return ( +
+ +