diff --git a/package-lock.json b/package-lock.json index 53262aad3..50366f05e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "react-dom": "^18.2.0", "react-router-dom": "^6.23.1", "react-scripts": "5.0.1", + "sass": "^1.77.6", "web-vitals": "^2.1.4" } }, @@ -9196,6 +9197,11 @@ "url": "https://opencollective.com/immer" } }, + "node_modules/immutable": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -15268,6 +15274,22 @@ "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" }, + "node_modules/sass": { + "version": "1.77.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", + "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/sass-loader": { "version": "12.6.0", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", diff --git a/package.json b/package.json index 48a05b3ce..e14270e89 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "react-dom": "^18.2.0", "react-router-dom": "^6.23.1", "react-scripts": "5.0.1", + "sass": "^1.77.6", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/src/App.jsx b/src/App.jsx index d41047767..cff9101ff 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -7,6 +7,7 @@ import Privacy from './pages/privacy/Privacy'; import Items from './pages/items/Items'; import AddItems from './pages/addItems/AddItems'; import NavigationBar from './components/navigationBar/NavigationBar'; +import ItemDetail from './pages/itemDetail/ItemDetail'; import './styles/global.css'; function App() { @@ -22,6 +23,7 @@ function App() { } /> } /> + } /> } /> diff --git a/src/assets/images/icons/ic_back.svg b/src/assets/images/icons/ic_back.svg new file mode 100644 index 000000000..b84ac950e --- /dev/null +++ b/src/assets/images/icons/ic_back.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/icons/ic_heart.svg b/src/assets/images/icons/ic_heart.svg index cad016c13..64594f464 100644 --- a/src/assets/images/icons/ic_heart.svg +++ b/src/assets/images/icons/ic_heart.svg @@ -1,3 +1,5 @@ - - + + + + diff --git a/src/assets/images/icons/ic_inquiry_empty.svg b/src/assets/images/icons/ic_inquiry_empty.svg new file mode 100644 index 000000000..8c1c6ba7e --- /dev/null +++ b/src/assets/images/icons/ic_inquiry_empty.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/components/items/BestItems/BestItems.jsx b/src/components/items/BestItems/BestItems.jsx index 081d270f1..f857c95f2 100644 --- a/src/components/items/BestItems/BestItems.jsx +++ b/src/components/items/BestItems/BestItems.jsx @@ -2,6 +2,7 @@ import './BestItems.css'; import { useEffect, useState } from 'react'; import Item from '../Item/Item'; import { getProducts } from '../../../pages/api/Items'; +import { useNavigate } from 'react-router-dom'; const getPageSize = () => { const width = window.innerWidth; @@ -24,6 +25,7 @@ function BestItems() { const [keyword, setKeyword] = useState(''); // 에러 const [fetchingError, setfetchingError] = useState(null); + const navigate = useNavigate(); const fetchItemList = async ({ order, page, pageSize, keyword }) => { try { @@ -35,10 +37,17 @@ function BestItems() { setfetchingError(err); } }; + const handleResize = () => { setPageSize(getPageSize()); }; + const handleClickItem = (e) => { + e.preventDefault(); + + navigate(`/items/${e.currentTarget.dataset.itemId}`); + }; + useEffect(() => { window.addEventListener('resize', handleResize); fetchItemList({ order, page, pageSize, keyword }); @@ -56,7 +65,11 @@ function BestItems() {
{fetchingError && fetchingError.message} {itemList?.map((item) => ( - + ))}
diff --git a/src/components/items/Item/Item.jsx b/src/components/items/Item/Item.jsx index a60035754..441d048ac 100644 --- a/src/components/items/Item/Item.jsx +++ b/src/components/items/Item/Item.jsx @@ -2,13 +2,13 @@ import React from 'react'; import { ReactComponent as IconHeart } from '../../../assets/images/icons/ic_heart.svg'; import ImgDefault from '../../../assets/images/market/img_default.png'; -function Item({ item }) { +function Item({ item, handleClick = () => {} }) { const handleErrorImage = (e) => { e.target.src = ImgDefault; }; return ( -
+
{item.name} +
+
+ +
+
+
+
+
+

{itemTitle}

+
+
+

{`${itemPrice}원`}

+
+
+ +
+
+

+ {'상품 소개'} +

+
+
+

{itemDesc}

+
+
+ {'상품 태그'} +
+
+ +
+
+
+
+
+ +
+
+
+
+ + ); +} + +export default ItemInfo; diff --git a/src/components/items/ItemInfo/ItemInfo.module.scss b/src/components/items/ItemInfo/ItemInfo.module.scss new file mode 100644 index 000000000..6b1cdff8f --- /dev/null +++ b/src/components/items/ItemInfo/ItemInfo.module.scss @@ -0,0 +1,53 @@ +@import '../../../styles/global.css'; +@import '../../../styles/global.scss'; + +.item-info { + display: flex; + margin: 0 0 32px; + justify-content: space-between; + align-items: flex-start; + + &__img { + width: 486px; + height: 486px; + } + + &__content { + display: flex; + margin: 0 0 0 24px; + width: 690px; + height: 486px; + flex-direction: column; + justify-content: space-between; + align-items: flex-start; + } + + &__title { + margin: 0 0 16px; + font-size: 24px; + font-weight: 600; + color: $gray-800; + } + + &__price { + margin: 0 0 16px; + font-size: 40px; + font-weight: 600; + color: $gray-800; + } + + &__content-title { + margin: 16px 0 0; + font-size: 14px; + font-weight: 500; + color: $gray-600; + } + + &__desc-content { + margin: 8px 0 0; + font-size: 16px; + font-weight: 400; + line-height: 140%; + color: $gray-800; + } +} diff --git a/src/components/items/SellingItems/SellingItems.jsx b/src/components/items/SellingItems/SellingItems.jsx index 4b063a2bb..e3c385125 100644 --- a/src/components/items/SellingItems/SellingItems.jsx +++ b/src/components/items/SellingItems/SellingItems.jsx @@ -5,6 +5,7 @@ import { getProducts } from '../../../pages/api/Items'; import Item from '../Item/Item'; import PaginationBar from '../PaginationBar/PaginationBar'; import './SellingItems.css'; +import { useNavigate } from 'react-router-dom'; const getPageSize = () => { const width = window.innerWidth; @@ -31,6 +32,7 @@ function SellingItems() { const [totalPageNum, setTotalPageNum] = useState(); // 에러 const [fetchingError, setfetchingError] = useState(null); + const navigate = useNavigate(); const handlePageChange = (pageNumber) => { setPage(pageNumber); @@ -49,6 +51,12 @@ function SellingItems() { } }; + const handleClickItem = (e) => { + e.preventDefault(); + + navigate(`/items/${e.currentTarget.dataset.itemId}`); + }; + const fetchItemList = async ({ order, page, pageSize, keyword }) => { try { setfetchingError(null); @@ -130,7 +138,11 @@ function SellingItems() { )}
{itemList?.map((item) => ( - + ))}
diff --git a/src/core/ui/buttons/Button/Button.jsx b/src/core/ui/buttons/Button/Button.jsx index 97763771a..cba8102f7 100644 --- a/src/core/ui/buttons/Button/Button.jsx +++ b/src/core/ui/buttons/Button/Button.jsx @@ -1,6 +1,14 @@ import { useEffect, useRef } from 'react'; + import './Button.css'; -function Button({ text = '버튼', onClick = () => {}, isDisabled = false }) { +function Button({ + text = '버튼', + onClick = () => {}, + isDisabled = false, + iconFront, + iconBack, + customBorderRound, +}) { const ref = useRef(); useEffect(() => { @@ -10,7 +18,9 @@ function Button({ text = '버튼', onClick = () => {}, isDisabled = false }) { return ( <> ); diff --git a/src/core/ui/buttons/FavoriteButton/FavoriteButton.jsx b/src/core/ui/buttons/FavoriteButton/FavoriteButton.jsx new file mode 100644 index 000000000..09417d2cf --- /dev/null +++ b/src/core/ui/buttons/FavoriteButton/FavoriteButton.jsx @@ -0,0 +1,24 @@ +import { useState } from 'react'; +import { ReactComponent as IconHeart } from '../../../../assets/images/icons/ic_heart.svg'; +import styles from './FavoriteButton.module.scss'; + +function FavoriteButton({ initCount = 0 }) { + const [count, setCount] = useState(initCount); + + const handleClick = () => { + setCount(count + 1); + }; + + return ( + <> + + + ); +} + +export default FavoriteButton; diff --git a/src/core/ui/buttons/FavoriteButton/FavoriteButton.module.scss b/src/core/ui/buttons/FavoriteButton/FavoriteButton.module.scss new file mode 100644 index 000000000..fccff7064 --- /dev/null +++ b/src/core/ui/buttons/FavoriteButton/FavoriteButton.module.scss @@ -0,0 +1,20 @@ +@import '../../../../styles/global.scss'; + +.btn { + border: 1px solid $gray-200; + border-radius: 35px; + + &__wrapper { + display: flex; + flex-direction: row; + align-items: center; + padding: 4px 12px; + } + + &__count { + margin: 0 0 0 5px; + font-size: 16px; + font-weight: 500; + color: $gray-800; + } +} diff --git a/src/core/ui/cards/ImageCard/ImageCard.jsx b/src/core/ui/cards/ImageCard/ImageCard.jsx new file mode 100644 index 000000000..5932de1cb --- /dev/null +++ b/src/core/ui/cards/ImageCard/ImageCard.jsx @@ -0,0 +1,29 @@ +import styles from './ImageCard.module.scss'; +import errImg from '../../../../assets/images/market/img_default.png'; + +function ImageCard({ + imageSrc, + ImgDeafault, + isRoundEdged = false, + isRound = false, +}) { + return ( + <> +
+ 상품 사진 { + e.target.src = errImg; + }} + /> +
+ + ); +} + +export default ImageCard; diff --git a/src/core/ui/cards/ImageCard/ImageCard.module.scss b/src/core/ui/cards/ImageCard/ImageCard.module.scss new file mode 100644 index 000000000..2b3128186 --- /dev/null +++ b/src/core/ui/cards/ImageCard/ImageCard.module.scss @@ -0,0 +1,17 @@ +.wrapper { + width: 100%; + height: 100%; +} + +.photo__img { + width: 100%; + height: 100%; +} + +.photo__img--round16 { + border-radius: 16px; +} + +.photo__img--round50percent { + border-radius: 50%; +} diff --git a/src/core/ui/comments/CommentItem/CommentItem.jsx b/src/core/ui/comments/CommentItem/CommentItem.jsx new file mode 100644 index 000000000..a92d5cd85 --- /dev/null +++ b/src/core/ui/comments/CommentItem/CommentItem.jsx @@ -0,0 +1,32 @@ +import ImageCard from '../../cards/ImageCard/ImageCard'; +import styles from './CommentItem.module.scss'; + +export default function CommentItem({ + profileImageSrc, + nickname = 'nickname', + content = 'content', + updatedAt = '0시간 전', +}) { + return ( + <> +
+

{content}

+
+
+ +
+
+
{nickname}
+
{updatedAt}
+
+
+
+ + ); +} diff --git a/src/core/ui/comments/CommentItem/CommentItem.module.scss b/src/core/ui/comments/CommentItem/CommentItem.module.scss new file mode 100644 index 000000000..f9c1c6cff --- /dev/null +++ b/src/core/ui/comments/CommentItem/CommentItem.module.scss @@ -0,0 +1,44 @@ +@import '../../../../styles/global.scss'; + +.comment { + display: flex; + width: 100%; + flex-direction: column; + justify-content: center; + align-items: flex-start; + + &__content { + width: 100%; + font-size: 16px; + font-weight: 400; + line-height: 140%; + color: $gray-800; + } + + &__wrapper-info { + display: flex; + margin: 24px 0 0; + } + + &__avatar { + width: 40px; + height: 40px; + } + + &__wrapper-nickname { + margin: 0 0 0 8px; + } + + &__nickname { + margin: 0 0 4px; + font-size: 14px; + font-weight: 400; + color: $gray-600; + } + + &__updated-at { + font-size: 12px; + font-weight: 400; + color: $gray-400; + } +} diff --git a/src/core/ui/comments/CommentList/CommentList.jsx b/src/core/ui/comments/CommentList/CommentList.jsx new file mode 100644 index 000000000..05bfe9a99 --- /dev/null +++ b/src/core/ui/comments/CommentList/CommentList.jsx @@ -0,0 +1,42 @@ +import HorizontalLine from '../../lines/HorizontalLine/HorizontalLine'; +import CommentItem from '../CommentItem/CommentItem'; +import styles from './CommentList.module.scss'; + +export default function CommentList({ + initCommentCount = 3, + comments = [], + emptyIcon, + emptyMessage = '', +}) { + return ( + <> +
+ {comments.length > 0 && + comments.map((comment) => ( +
+
+ +
+ +
+ ))} + {comments.length < 1 && ( +
+ {emptyIcon} + + {emptyMessage} + +
+ )} +
+ + ); +} diff --git a/src/core/ui/comments/CommentList/CommentList.module.scss b/src/core/ui/comments/CommentList/CommentList.module.scss new file mode 100644 index 000000000..17e624149 --- /dev/null +++ b/src/core/ui/comments/CommentList/CommentList.module.scss @@ -0,0 +1,32 @@ +@import '../../../../styles/global.scss'; + +.list { + margin: 24px 0 0; + + &__wrapper-item { + margin: 0 0 24px; + } + + &__item { + margin: 0 0 24px; + } + + &__item:last-child { + margin: 0; + } + + &__wrapper-empty { + display: flex; + margin: 0; + flex-direction: column; + justify-content: center; + align-items: center; + } + + &__empty-message { + font-size: 14px; + font-weight: 200; + line-height: 24px; + color: $gray-400; + } +} diff --git a/src/core/ui/inputs/SimpleInput/SimpleInput.jsx b/src/core/ui/inputs/SimpleInput/SimpleInput.jsx new file mode 100644 index 000000000..37b8dd6fb --- /dev/null +++ b/src/core/ui/inputs/SimpleInput/SimpleInput.jsx @@ -0,0 +1,23 @@ +import styles from './SimpleInput.module.scss'; + +export default function SimpleInput({ + lable = '', + name = '', + type = 'text', + placeholder = '', + isLabelVisible = true, +}) { + return ( + <> +
+ {isLabelVisible && } + +
+ + ); +} diff --git a/src/core/ui/inputs/SimpleInput/SimpleInput.module.scss b/src/core/ui/inputs/SimpleInput/SimpleInput.module.scss new file mode 100644 index 000000000..84852a6ba --- /dev/null +++ b/src/core/ui/inputs/SimpleInput/SimpleInput.module.scss @@ -0,0 +1,35 @@ +@import '../../../../styles/global.scss'; + +.wrapper-input { + display: flex; + width: 100%; + justify-content: center; + align-items: flex-start; + flex-direction: column; +} + +.lable { + margin: 0 0 16px; + font-size: 16px; + font-weight: 600; + color: $gray-900; +} + +.input { + display: flex; + align-items: flex-start; + padding: 16px 24px; + width: 100%; + height: 104px; + border-radius: 12px; + background-color: $gray-50; + border: none; + + &::placeholder, + &::-webkit-input-placeholder, + &:-moz-placeholder, + &::-moz-placeholder, + &:-ms-input-placeholder { + color: blue; + } +} diff --git a/src/core/ui/lines/HorizontalLine/HorizontalLine.jsx b/src/core/ui/lines/HorizontalLine/HorizontalLine.jsx new file mode 100644 index 000000000..b9d6c2d08 --- /dev/null +++ b/src/core/ui/lines/HorizontalLine/HorizontalLine.jsx @@ -0,0 +1,11 @@ +import styles from './HorizontalLine.module.scss'; + +function HorizontalLine() { + return ( + <> +
+ + ); +} + +export default HorizontalLine; diff --git a/src/core/ui/lines/HorizontalLine/HorizontalLine.module.scss b/src/core/ui/lines/HorizontalLine/HorizontalLine.module.scss new file mode 100644 index 000000000..c0dba0198 --- /dev/null +++ b/src/core/ui/lines/HorizontalLine/HorizontalLine.module.scss @@ -0,0 +1,7 @@ +@import '../../../../styles/global.scss'; + +.line { + width: 100%; + height: 1px; + background-color: $gray-200; +} diff --git a/src/core/ui/tags/TagList/TagList.jsx b/src/core/ui/tags/TagList/TagList.jsx new file mode 100644 index 000000000..f4c870116 --- /dev/null +++ b/src/core/ui/tags/TagList/TagList.jsx @@ -0,0 +1,23 @@ +import styles from './TagList.module.scss'; + +function TagList({ tags = [], isSharpVisible }) { + return ( + <> + {tags.length > 0 && ( +
+ {tags.map((tag, i) => ( +
+ {`${isSharpVisible ? '#' : ''}${tag}`} +
+ ))} +
+ )} + + ); +} + +export default TagList; diff --git a/src/core/ui/tags/TagList/TagList.module.scss b/src/core/ui/tags/TagList/TagList.module.scss new file mode 100644 index 000000000..351bdf8bb --- /dev/null +++ b/src/core/ui/tags/TagList/TagList.module.scss @@ -0,0 +1,19 @@ +@import '../../../../styles/global.scss'; +.tag-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: flex-start; + width: 100%; + margin: 0; +} + +.tag-list__tag { + display: flex; + align-items: center; + margin: 12px 12px 0 0; + padding: 6px 16px; + border-radius: 26px; + color: $gray-800; + background-color: $gray-50; +} diff --git a/src/lib/items/hooks/useItem.js b/src/lib/items/hooks/useItem.js new file mode 100644 index 000000000..499fea4ea --- /dev/null +++ b/src/lib/items/hooks/useItem.js @@ -0,0 +1,48 @@ +import { useEffect, useState } from 'react'; +import { getProductById } from '../../../pages/api/Items'; + +/** + * + * @param {number} productId + * @param {array} deps + * @returns [imgSrc, title, price, desc, tags, favoriteCount] + */ +export function useItem(productId = 0, deps = []) { + const [imgSrc, setImgSrc] = useState(''); + const [title, setTitle] = useState(''); + const [price, setPrice] = useState(0); + const [desc, setDesc] = useState(''); + const [tags, setTags] = useState([]); + const [isFavorite, setIsFavorite] = useState(false); + const [favoriteCount, setFavoriteCount] = useState(0); + + const fetchProduct = async () => { + try { + const result = await getProductById(productId); + setImgSrc(result.images[0]); + setTitle(result.name); + setPrice(result.price); + setDesc(result.description); + setTags(result.tags); + setIsFavorite(result.isFavorite); + setFavoriteCount(result.favoriteCount); + } catch (error) { + // 디폴트 값 + setImgSrc( + 'https://www.shutterstock.com/image-vector/default-ui-image-placeholder-wireframes-600nw-1037719192.jpg' + ); + setTitle('-'); + setPrice(0); + setDesc('-'); + setTags([]); + setIsFavorite(false); + setFavoriteCount(0); + } + }; + + useEffect(() => { + fetchProduct(); + }, deps); + + return [imgSrc, title, price, desc, tags, isFavorite, favoriteCount]; +} diff --git a/src/lib/items/hooks/useItemComments.js b/src/lib/items/hooks/useItemComments.js new file mode 100644 index 000000000..4acf5e4d2 --- /dev/null +++ b/src/lib/items/hooks/useItemComments.js @@ -0,0 +1,31 @@ +import { useEffect, useState } from 'react'; +import { getCommentsByProductId } from '../../../pages/api/Items'; + +/** + * + * @param {number} productId + * @param {array} deps + * @returns [imgSrc, title, price, desc, tags, favoriteCount] + */ +export function useItemComments(productId = 0, limit = 1, cursor, deps = []) { + const [comments, setComments] = useState([]); + const [nextCursor, setNextCursor] = useState(null); + + const fetchData = async () => { + try { + const result = await getCommentsByProductId(productId, limit, cursor); + setComments(result.list); + setNextCursor(result.nextCursor); + } catch (error) { + // 디폴트 값 + setComments([]); + setNextCursor(null); + } + }; + + useEffect(() => { + fetchData(); + }, deps); + + return [comments, nextCursor]; +} diff --git a/src/pages/addItems/AddItems.jsx b/src/pages/addItems/AddItems.jsx index 50be45d80..c7f3cd560 100644 --- a/src/pages/addItems/AddItems.jsx +++ b/src/pages/addItems/AddItems.jsx @@ -98,7 +98,7 @@ function AddItems() { type="text" name="name" value={values.name} - placeholder="상품명을 적어주세요." + placeholder="상품명을 입력해주세요" onChange={handleInputChange} />
@@ -110,7 +110,7 @@ function AddItems() { className="input-form-add-item input-textarea" name="desc" value={values.desc} - placeholder="상품 소개를 적어주세요." + placeholder="상품 소개를 입력해주세요" onChange={handleInputChange} />
@@ -123,7 +123,7 @@ function AddItems() { type="number" name="price" value={values.price} - placeholder="판매가격을 적어주세요." + placeholder="판매가격을 입력해주세요" onChange={handleInputChange} />
diff --git a/src/pages/api/Items.js b/src/pages/api/Items.js index 9af5ac39b..f817da1fd 100644 --- a/src/pages/api/Items.js +++ b/src/pages/api/Items.js @@ -39,3 +39,46 @@ export async function getProducts({ const body = await response.json(); return body; } + +/** + * 상품 상세 조회 + * @param {number} productId 상품 번호 + * @returns json. ex) + * { + "createdAt": "2024-07-05T04:42:58.556Z", + "favoriteCount": 0, + "ownerId": 1, + "images": [ + "https://example.com/..." + ], + "tags": [ + "전자제품" + ], + "price": 0, + "description": "string", + "name": "상품 이름", + "id": 1, + "isFavorite": true +} + */ +export async function getProductById(productId = 0) { + const param = `${productId}`; + const response = await fetch(`${BASE_URL}/products/${param}`); + const body = await response.json(); + return body; +} + +export async function getCommentsByProductId( + productId = 0, + limit = 1, + nextCursor +) { + const param = `${productId}`; + let query = `?limit=${limit}`; + if (nextCursor) query += `&cursor=${nextCursor}`; + const response = await fetch( + `${BASE_URL}/products/${param}/comments${query}` + ); + const body = await response.json(); + return body; +} diff --git a/src/pages/itemDetail/ItemDetail.jsx b/src/pages/itemDetail/ItemDetail.jsx new file mode 100644 index 000000000..29208419d --- /dev/null +++ b/src/pages/itemDetail/ItemDetail.jsx @@ -0,0 +1,76 @@ +import { useNavigate, useParams } from 'react-router-dom'; +import { useItem } from '../../lib/items/hooks/useItem'; +import ItemInfo from '../../components/items/ItemInfo/ItemInfo'; +import HorizontalLine from '../../core/ui/lines/HorizontalLine/HorizontalLine'; +import Button from '../../core/ui/buttons/Button/Button'; +import CommentList from '../../core/ui/comments/CommentList/CommentList'; +import { ReactComponent as IconBack } from '../../assets/images/icons/ic_back.svg'; +import { ReactComponent as IconEmptyComment } from '../../assets/images/icons/ic_inquiry_empty.svg'; +import styles from './ItemDetail.module.scss'; +import { useItemComments } from '../../lib/items/hooks/useItemComments'; +import SimpleInput from '../../core/ui/inputs/SimpleInput/SimpleInput'; + +function ItemDetail() { + const { productId } = useParams(); + const navigate = useNavigate(); + + const [imgSrc, title, price, desc, tags, isFavorite, favoriteCount] = useItem( + productId, + [] + ); + + const [comments, nextCursor] = useItemComments(productId, 3, undefined, []); + + return ( + <> +
+ + + +
+ +
+
+ + + } + emptyMessage={'아직 문의가 없습니다.'} + /> +
+
+
+ + ); +} + +export default ItemDetail; diff --git a/src/pages/itemDetail/ItemDetail.module.scss b/src/pages/itemDetail/ItemDetail.module.scss new file mode 100644 index 000000000..bb34aebe8 --- /dev/null +++ b/src/pages/itemDetail/ItemDetail.module.scss @@ -0,0 +1,21 @@ +.item-detail { + max-width: 1200px; + margin: 94px auto; + padding: 0 16px; + + &__btn-to-list { + display: flex; + margin: 40px 0 0; + justify-content: center; + } +} + +.comment-form { + margin: 24px 0 0; + + &__btn-submit { + display: flex; + margin: 16px 0 0; + justify-content: end; + } +} diff --git a/src/styles/global.scss b/src/styles/global.scss new file mode 100644 index 000000000..c8a945dc4 --- /dev/null +++ b/src/styles/global.scss @@ -0,0 +1,12 @@ +$gray-900: #1b1d1f; +$gray-800: #26282b; +$gray-600: #454c53; +$gray-500: #72787f; +$gray-400: #9ea4a8; +$gray-200: #e5e7eb; +$gray-100: #e8ebed; +$gray-50: #f7f7f8; +$gray-10: #f3f4f6; +$gray-heart: #4b5563; + +$blue: #3692ff;