diff --git a/vite-project/package-lock.json b/vite-project/package-lock.json index 58b22412..2d7d880b 100644 --- a/vite-project/package-lock.json +++ b/vite-project/package-lock.json @@ -2884,9 +2884,9 @@ } }, "node_modules/react-router": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.7.0.tgz", - "integrity": "sha512-3FUYSwlvB/5wRJVTL/aavqHmfUKe0+Xm9MllkYgGo9eDwNdkvwlJGjpPxono1kCycLt6AnDTgjmXvK3/B4QGuw==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.8.2.tgz", + "integrity": "sha512-7M2fR1JbIZ/jFWqelpvSZx+7vd7UlBTfdZqf6OSdF9g6+sfdqJDAWcak6ervbHph200ePlu+7G8LdoiC3ReyAQ==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", diff --git a/vite-project/public/images/ic_back.svg b/vite-project/public/images/ic_back.svg new file mode 100644 index 00000000..9ba5ad94 --- /dev/null +++ b/vite-project/public/images/ic_back.svg @@ -0,0 +1,4 @@ + + + + diff --git a/vite-project/public/images/ic_kebab.svg b/vite-project/public/images/ic_kebab.svg new file mode 100644 index 00000000..dd7ed7f5 --- /dev/null +++ b/vite-project/public/images/ic_kebab.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/vite-project/src/App.css b/vite-project/src/App.css index cd559b3c..596fa193 100644 --- a/vite-project/src/App.css +++ b/vite-project/src/App.css @@ -3,9 +3,17 @@ --primary200: #1967d6; --primary300: #1251aa; + --secondary200: #e5e7eb; + --secondary300: #e5e7eb; + --secondary400: #9ca3af; + --secondary500: #6b7280; + --secondary600: #4b5563; + --secondary800: #1f2937; + --gray50: #f9fafb; --gray100: #f3f4f6; --gray200: #e5e7eb; + --gray300: #d1d5db; --gray400: #9ca3af; --gray500: #6b7280; --gray600: #4b5563; @@ -70,8 +78,10 @@ p { } .btn-medium { + width: 240px; height: 48px; - padding: 12px 71px; + padding: 12px 64px; + white-space: nowrap; border-radius: 40px; border: none; font-weight: 600; @@ -117,6 +127,12 @@ p { align-items: center; } +.kebab-btn { + display: flex; + justify-content: center; + align-items: center; +} + .container { max-width: 1200px; margin: 0 auto; diff --git a/vite-project/src/App.jsx b/vite-project/src/App.jsx index 8ad11bfe..7fe10905 100644 --- a/vite-project/src/App.jsx +++ b/vite-project/src/App.jsx @@ -1,9 +1,10 @@ import "./App.css"; import { Route, Routes, Navigate } from "react-router"; -import ItemsPage from "./pages/ItemsPage"; -import AddItemPage from "./pages/AddItemPage"; +import ProductsPage from "./pages/ProductsPage"; +import AddProductPage from "./pages/AddProductPage"; import Layout from "./layout/Layout"; import FreeBoard from "./pages/FreeBoardPage"; +import ProductDetailPage from "./pages/ProductDetailPage"; function App() { return ( @@ -12,10 +13,13 @@ function App() { } /> }> - } /> - } /> + + } /> + } /> + + } /> {/* freeBoard : link active 테스트를 위해 미리 만듦 */} - } />{" "} + } /> ); diff --git a/vite-project/src/common/Dropdown/index.jsx b/vite-project/src/common/Dropdown/index.jsx index e94bafbb..f3752fdf 100644 --- a/vite-project/src/common/Dropdown/index.jsx +++ b/vite-project/src/common/Dropdown/index.jsx @@ -1,15 +1,9 @@ -import React, { useState } from "react"; - const DropDown = ({ children }) => { return
{children}
; }; -DropDown.header = ({ children, onClick }) => { - return ( - - ); +DropDown.header = ({ children }) => { + return
{children}
; }; DropDown.menus = ({ children, isOpen }) => { diff --git a/vite-project/src/common/ProductCard/index.jsx b/vite-project/src/common/ProductCard/index.jsx index 35795ae4..e803d0a6 100644 --- a/vite-project/src/common/ProductCard/index.jsx +++ b/vite-project/src/common/ProductCard/index.jsx @@ -1,4 +1,3 @@ -import React from "react"; import "./ProductCard.style.css"; const ProductCard = ({ product, category }) => { diff --git a/vite-project/src/common/ProductList/index.jsx b/vite-project/src/common/ProductList/index.jsx index 10308393..21278c2f 100644 --- a/vite-project/src/common/ProductList/index.jsx +++ b/vite-project/src/common/ProductList/index.jsx @@ -1,4 +1,3 @@ -import React from "react"; import { Link } from "react-router"; import ProductCard from "../ProductCard"; const ProductList = ({ products, className }) => { @@ -6,7 +5,7 @@ const ProductList = ({ products, className }) => {
    {products?.map((product) => (
  • - +
  • diff --git a/vite-project/src/common/ScrollToTop/index.jsx b/vite-project/src/common/ScrollToTop/index.jsx new file mode 100644 index 00000000..ab426d23 --- /dev/null +++ b/vite-project/src/common/ScrollToTop/index.jsx @@ -0,0 +1,14 @@ +import { useEffect } from "react"; +import { useLocation } from "react-router"; + +const ScrollToTop = () => { + const { pathname } = useLocation(); + + useEffect(() => { + window.scrollTo(0, 0); + }, [pathname]); + + return null; +}; + +export default ScrollToTop; diff --git a/vite-project/src/common/TagBadge/TagBadge.style.css b/vite-project/src/common/TagBadge/TagBadge.style.css index f5a936b8..979e5f19 100644 --- a/vite-project/src/common/TagBadge/TagBadge.style.css +++ b/vite-project/src/common/TagBadge/TagBadge.style.css @@ -6,6 +6,7 @@ display: inline-flex; justify-content: center; align-items: center; + white-space: nowrap; gap: 10px; } diff --git a/vite-project/src/common/TagBadge/index.jsx b/vite-project/src/common/TagBadge/index.jsx index ddda26e9..e7f80fcd 100644 --- a/vite-project/src/common/TagBadge/index.jsx +++ b/vite-project/src/common/TagBadge/index.jsx @@ -4,9 +4,11 @@ const TagBadge = ({ name, onDelete }) => { return (
    #{name} - + {onDelete && ( + + )}
    ); }; diff --git a/vite-project/src/common/TagList/index.jsx b/vite-project/src/common/TagList/index.jsx index f5443253..93d6926a 100644 --- a/vite-project/src/common/TagList/index.jsx +++ b/vite-project/src/common/TagList/index.jsx @@ -6,7 +6,10 @@ const TagList = ({ tags, onDelete }) => {
      {tags?.map((tag, index) => (
    • - onDelete(index)} /> + onDelete(index) : undefined} + />
    • ))}
    diff --git a/vite-project/src/common/UserInfo/Userinfo.style.css b/vite-project/src/common/UserInfo/Userinfo.style.css new file mode 100644 index 00000000..3622a31c --- /dev/null +++ b/vite-project/src/common/UserInfo/Userinfo.style.css @@ -0,0 +1,22 @@ +.userInfo-badge { + display: flex; + align-items: center; + gap: 16px; +} + +.userInfo-badge-name-date { + display: flex; + flex-direction: column; + gap: 2px; + font-size: 14px; +} + +.userInfo-badge-name { + color: var(--secondary600); + font-weight: 500; +} + +.userInfo-badge-createdAt { + color: var(--gray400); + font-weight: 400; +} diff --git a/vite-project/src/common/UserInfo/index.jsx b/vite-project/src/common/UserInfo/index.jsx new file mode 100644 index 00000000..0247033e --- /dev/null +++ b/vite-project/src/common/UserInfo/index.jsx @@ -0,0 +1,16 @@ +import "./UserInfo.style.css"; + +const UserInfo = ({ image, name, date }) => { + const imageSrc = image || "/images/nav-panda.svg"; + return ( +
    + 이미지 +
    + {name} + {date} +
    +
    + ); +}; + +export default UserInfo; diff --git a/vite-project/src/constants/PRODUCTS.js b/vite-project/src/constants/PRODUCTS.js index c08eb7c1..199f9aa5 100644 --- a/vite-project/src/constants/PRODUCTS.js +++ b/vite-project/src/constants/PRODUCTS.js @@ -2,4 +2,7 @@ export const ORDER_BYS = { 최신순: "recent", 좋아요순: "favorite", }; + +export const DROPDOWN_MENUS = Object.keys(ORDER_BYS); + export const GROUP_SIZE = 5; diff --git a/vite-project/src/hooks/useDeleteProductCommentQuery.js b/vite-project/src/hooks/useDeleteProductCommentQuery.js new file mode 100644 index 00000000..fa940854 --- /dev/null +++ b/vite-project/src/hooks/useDeleteProductCommentQuery.js @@ -0,0 +1,20 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import instance from "../api/axiosInstance"; + +const deleteProductComment = ({ commentId }) => { + return instance.delete(`comments/${commentId}`); +}; + +export const useDeleteProductCommentQuery = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: deleteProductComment, + onSuccess: () => { + queryClient.invalidateQueries(["getProductComments"]); + }, + onError: (error) => { + console.log(error); + }, + }); +}; diff --git a/vite-project/src/hooks/useGetProductComments.js b/vite-project/src/hooks/useGetProductComments.js new file mode 100644 index 00000000..3acf069f --- /dev/null +++ b/vite-project/src/hooks/useGetProductComments.js @@ -0,0 +1,17 @@ +import { useQuery } from "@tanstack/react-query"; +import instance from "../api/axiosInstance"; + +const getProductComments = ({ productId, limit, cursor }) => { + return instance.get(`products/${productId}/comments`, { + params: { limit, cursor }, + }); +}; + +export const useGetProductCommentsQuery = ({ productId, limit, cursor }) => { + return useQuery({ + queryKey: ["getProductComments", productId, limit], + queryFn: () => getProductComments({ productId, limit, cursor }), + staleTime: 300000, + select: (response) => response.data, + }); +}; diff --git a/vite-project/src/hooks/useGetProductDetail.js b/vite-project/src/hooks/useGetProductDetail.js new file mode 100644 index 00000000..59a5ebba --- /dev/null +++ b/vite-project/src/hooks/useGetProductDetail.js @@ -0,0 +1,15 @@ +import { useQuery } from "@tanstack/react-query"; +import instance from "../api/axiosInstance"; + +const getProductDetail = ({ productId }) => { + return instance.get(`products/${productId}`); +}; + +export const useGetProductDetailQuery = ({ productId }) => { + return useQuery({ + queryKey: ["getProductDetail", productId], + queryFn: () => getProductDetail({ productId }), + staleTime: 300000, + select: (response) => response.data, + }); +}; diff --git a/vite-project/src/hooks/useGetProducts.js b/vite-project/src/hooks/useGetProducts.js index 13c36521..6c675255 100644 --- a/vite-project/src/hooks/useGetProducts.js +++ b/vite-project/src/hooks/useGetProducts.js @@ -1,8 +1,8 @@ import { useQuery } from "@tanstack/react-query"; import instance from "../api/axiosInstance"; -const getProducts = async ({ page = 1, orderBy = "recent", pageSize = 10 }) => { - return await instance.get("/products", { +const getProducts = ({ page = 1, orderBy = "recent", pageSize = 10 }) => { + return instance.get("/products", { params: { page, orderBy, pageSize }, }); }; diff --git a/vite-project/src/main.jsx b/vite-project/src/main.jsx index 4025443e..41be83a7 100644 --- a/vite-project/src/main.jsx +++ b/vite-project/src/main.jsx @@ -2,12 +2,14 @@ import { createRoot } from "react-dom/client"; import { BrowserRouter } from "react-router"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import App from "./App.jsx"; +import ScrollToTop from "./common/ScrollToTop/index.jsx"; const queryClient = new QueryClient(); createRoot(document.getElementById("root")).render( + diff --git a/vite-project/src/pages/AddItemPage/components/AddItemFormHeader/index.jsx b/vite-project/src/pages/AddItemPage/components/AddItemFormHeader/index.jsx deleted file mode 100644 index 53e28e2a..00000000 --- a/vite-project/src/pages/AddItemPage/components/AddItemFormHeader/index.jsx +++ /dev/null @@ -1,22 +0,0 @@ -const AddItemFormHeader = ({ formData }) => { - const isDisabled = - formData.name && - formData.price && - formData.description && - formData.tags.length > 0; - - return ( -
    -

    상품 등록하기

    - -
    - ); -}; - -export default AddItemFormHeader; diff --git a/vite-project/src/pages/AddItemPage/components/AddItemTagList/index.jsx b/vite-project/src/pages/AddItemPage/components/AddItemTagList/index.jsx deleted file mode 100644 index e9f81799..00000000 --- a/vite-project/src/pages/AddItemPage/components/AddItemTagList/index.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import TagBadge from "../../../../common/TagBadge"; - -const AddItemTagList = ({ tags }) => { - return ( - - ); -}; - -export default AddItemTagList; diff --git a/vite-project/src/pages/AddItemPage/AddItemPage.style.css b/vite-project/src/pages/AddProductPage/AddProductPage.style.css similarity index 64% rename from vite-project/src/pages/AddItemPage/AddItemPage.style.css rename to vite-project/src/pages/AddProductPage/AddProductPage.style.css index dc13d896..7396b3f9 100644 --- a/vite-project/src/pages/AddItemPage/AddItemPage.style.css +++ b/vite-project/src/pages/AddProductPage/AddProductPage.style.css @@ -1,4 +1,4 @@ -.addItem-page-layout { +.addProduct-page-layout { margin-top: 90px; display: flex; flex-direction: column; @@ -6,35 +6,35 @@ padding-bottom: 50px; } -.addItem-form { +.addProduct-form { display: flex; flex-direction: column; gap: 16px; } -.addItem-form-header { +.addProduct-form-header { display: flex; justify-content: space-between; align-items: center; } -.addItem-form-header-title { +.addProduct-form-header-title { font-weight: 700; font-size: 20px; color: var(--gray800); } -.addItem-form-submit-btn { +.addProduct-form-submit-btn { background-color: var(--primary100); } -.addItem-form-submit-btn:disabled { +.addProduct-form-submit-btn:disabled { background-color: var(--gray400); } -.addItem-input, -.addItem-textArea, -.addItem-image-input { +.addProduct-input, +.addProduct-textArea, +.addProduct-image-input { background-color: var(--gray100); padding: 16px 24px; border-radius: 12px; @@ -44,12 +44,12 @@ color: #1f2937; } -.addItem-input, -.addItem-textArea { +.addProduct-input, +.addProduct-textArea { width: 100%; } -.addItem-image-input { +.addProduct-image-input { cursor: pointer; display: flex; flex-direction: column; @@ -59,38 +59,38 @@ color: var(--gray400); } -.addItem-image-input, -.addItem-image-card { +.addProduct-image-input, +.addProduct-image-card { aspect-ratio: 1; width: 282px; } -.addItem-textArea { +.addProduct-textArea { height: 282px; resize: none; } -.addItem-image-area { +.addProduct-image-area { display: flex; gap: 16px; } -.addItem-image-card { +.addProduct-image-card { position: relative; } -.addItem-image-card > button { +.addProduct-image-card > button { position: absolute; right: 10px; top: 10px; } @media screen and (max-width: 767px) { - .addItem-image-input, - .addItem-image-card { + .addProduct-image-input, + .addProduct-image-card { width: 160px; } - .addItem-image-area { + .addProduct-image-area { gap: 10px; } } diff --git a/vite-project/src/pages/AddItemPage/components/AddItemDescription/index.jsx b/vite-project/src/pages/AddProductPage/components/AddProductDescription/index.jsx similarity index 61% rename from vite-project/src/pages/AddItemPage/components/AddItemDescription/index.jsx rename to vite-project/src/pages/AddProductPage/components/AddProductDescription/index.jsx index d7a0ec23..f195400e 100644 --- a/vite-project/src/pages/AddItemPage/components/AddItemDescription/index.jsx +++ b/vite-project/src/pages/AddProductPage/components/AddProductDescription/index.jsx @@ -1,10 +1,10 @@ -const AddItemDescription = ({ value, onChange }) => { +const AddProductDescription = ({ value, onChange }) => { return (

    상품 소개