From b2e37bd7db278fa146bfb7395203b4a25bb9d4d1 Mon Sep 17 00:00:00 2001 From: hanseulhee <3021062@gmail.com> Date: Tue, 10 Oct 2023 14:15:18 +0900 Subject: [PATCH 01/10] reset --- .github/delete-merged-branch-config.yml | 15 ++++++++++++++ .github/pull_request_template.md | 27 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 .github/delete-merged-branch-config.yml create mode 100644 .github/pull_request_template.md diff --git a/.github/delete-merged-branch-config.yml b/.github/delete-merged-branch-config.yml new file mode 100644 index 000000000..2a6d27bef --- /dev/null +++ b/.github/delete-merged-branch-config.yml @@ -0,0 +1,15 @@ +name: delete branch on close pr + +on: + pull_request: + types: [closed] + +permissions: + pull-requests: write + +jobs: + delete-branch: + runs-on: ubuntu-latest + steps: + - name: delete branch + uses: SvanBoxel/delete-merged-branch@main diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..a85814779 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,27 @@ +## 요구사항 + +### 기본 + +- [x] +- [] +- [] + +### 심화 + +- [x] +- [] + +## 주요 변경사항 + +- +- + +## 스크린샷 + +![image](이미지url) + +## 멘토에게 + +- +- +- 셀프 코드 리뷰를 통해 질문 이어가겠습니다. From e11e25f095cf9fa1909206fd16ef114c00c9442a Mon Sep 17 00:00:00 2001 From: hanseulhee <3021062@gmail.com> Date: Tue, 10 Oct 2023 14:47:30 +0900 Subject: [PATCH 02/10] =?UTF-8?q?fix:=20=EB=A8=B8=EC=A7=80=20=ED=9B=84=20?= =?UTF-8?q?=EB=B8=8C=EB=9E=9C=EC=B9=98=20=EC=82=AD=EC=A0=9C=20github=20act?= =?UTF-8?q?ion=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/delete-merged-branch-config.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/delete-merged-branch-config.yml b/.github/delete-merged-branch-config.yml index 2a6d27bef..d54933615 100644 --- a/.github/delete-merged-branch-config.yml +++ b/.github/delete-merged-branch-config.yml @@ -4,12 +4,11 @@ on: pull_request: types: [closed] -permissions: - pull-requests: write - jobs: delete-branch: runs-on: ubuntu-latest steps: - name: delete branch uses: SvanBoxel/delete-merged-branch@main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 212e8643d8aebe6cab293e61e2800c4e9b5d6d88 Mon Sep 17 00:00:00 2001 From: hanseulhee <3021062@gmail.com> Date: Tue, 10 Oct 2023 14:50:26 +0900 Subject: [PATCH 03/10] =?UTF-8?q?env:=20workflows=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/{ => workflows}/delete-merged-branch-config.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{ => workflows}/delete-merged-branch-config.yml (100%) diff --git a/.github/delete-merged-branch-config.yml b/.github/workflows/delete-merged-branch-config.yml similarity index 100% rename from .github/delete-merged-branch-config.yml rename to .github/workflows/delete-merged-branch-config.yml From 1cf092590990394b88da3e9361482c9719121ba0 Mon Sep 17 00:00:00 2001 From: baehyunji Date: Fri, 26 Apr 2024 22:59:57 +0900 Subject: [PATCH 04/10] =?UTF-8?q?fix:=ED=94=BC=EB=93=9C=EB=B0=B1=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api.js | 7 ++-- src/assets/favorite.png | Bin 341 -> 708 bytes src/assets/search.png | Bin 355 -> 483 bytes src/components/App.module.css | 5 +-- src/components/BestProductsList.css | 10 +----- src/components/BestProductsList.js | 24 ++------------ src/components/Nav.js | 17 +++++----- src/components/Nav.module.css | 7 +++- src/components/ProductsList.css | 10 +----- src/components/ProductsList.js | 24 ++------------ src/components/ToggleMenu.js | 23 ++++++++++---- src/components/ToggleMenu.module.css | 19 +++++++++-- src/pages/UsedMarketPage.css | 24 +++++++++++--- src/pages/UsedMarketPage.js | 46 ++++++++++++++++++--------- 14 files changed, 110 insertions(+), 106 deletions(-) diff --git a/src/api.js b/src/api.js index c2ef4bf4f..0d279986a 100644 --- a/src/api.js +++ b/src/api.js @@ -1,7 +1,8 @@ export async function getProducts() { - const response = await fetch( - " https://panda-market-api.vercel.app/products/" - ); + const response = await fetch("https://panda-market-api.vercel.app/products/"); + if (!response.ok) { + throw new Error("데이터를 불러오는데 실패했습니다"); + } const body = await response.json(); return body; } diff --git a/src/assets/favorite.png b/src/assets/favorite.png index aea88e92a354a253ba8ceb1b01a415f11eeff5d1..35254c5abc2de6fa729244e880b3f1b98b6b8b74 100644 GIT binary patch literal 708 zcmV;#0z3VQP)l3Fn3VpcV}EA%`(}1$2cl;@0F^a)7#Lq`+K166ric}d;+UGBtQm_v z2#j~D=mNV780N7iWTwlGHJ=|8IIf!uG}YNKnzecN%c{E0+`H9uJ)PqlP{^8B&dfB9 znaYVH0)2-(Fz1e(X{0oj+by#x9}^uoF~t_1UkSNFMD147gT1gG(-!tzDn*`#9TDae z&Ro)|1*QtpGmwopm`Wd7=0{>1_QTOm>GcHORuHoz|7eX#?1MoV4F_Sjxa)7izW1AH zf^m2!0+pkEX>rzNYfVH8fVUQ&ZpxP?Ph*YAxJdPs%kk3Rv;PV2qfX0g(F!;|HP>?a z;)pn!zJol@FrrnVryRd#iIEaOMlgBl3wjb_@}x;JX)eI5x@htf;wn86M}d(f@Q|*( zWNt2i%&}-9tyKdVRiGsyj}Iw&?gncV+!L@aByN)2g{G@zL}aadAMjpMxbxnO5?e04 zaMGW7FI1VwoV) z%OXFjHv*-Ul$IA9(?Q!BySOHowGo6PhOKcdU?>~n8Q3450PL0L?!aM1M qyoYEiSv6}V)as@x7fa9hpZEh=YW-^CeMAKS0000IWh literal 341 zcmV-b0jmCqP)4cL zJx>Bb5Z(W0O+qP9!j*8LLL?T53C&SlNX}4rB-ns4HkW8aVq!x^{c6Ro?c{?8lzczi$&}{Wksy5IcBv|KLtT$VUofRq)znjby4nuD^8l!j~AiaOU z^8Eu>H<18KwFZLvC0^ea$X@0s*FrQedO|h~GGm_aTsCX>=tl`U*U?El-|R#nd!EC@ zx2b&1u{;j(oo)of%yE)>;D=|{8MeAGY)VjXA6`5j8%QC;dRTNk8ha=J)ymaS>Xxpq zR-az7cAi+85b3aEROYuiL$xovmszTk#vM_~BytIK0eI&ukH4Dal7^t7BvQ00000NkvXXu0mjfzNC%U diff --git a/src/assets/search.png b/src/assets/search.png index 003858fa5f8ca0638b0f6ed9f200ccc733fc0118..e9a58c7bfec04ba4b9a2618f5373ae2876c3e1bf 100644 GIT binary patch delta 458 zcmV;*0X6>P0^w{T$$N+X!C5>Ei0 z09~1grV(OXs41R+dIIYS;0WDAJI<>u0Tur+Ni&nz_nY^vv|Ke^-cNbaANXw|bac(kZ^Q`Gn&1%8HW<^yggV|=;R>9AJ%L{4MXI;< zR!{KmeLIOU7ByC4U?*=tsnh}{QjZ!nrLZOz4HBSnph|kpkUMsDF)QE{KZGl45{4^U z?d#!VHHBf&mVcJ}S?YJutf@d!1U4%lFyP1w1^9b}3@+ucC?N|QgoZ;c{U3}&22E0! zfoI9(zV}f6jk*~;)g@Uxo8P8zVUBaj5t#+=QpUCp)FWjwdRl4I-3?OMbycJXFBi9p zCdT;~mLN-;NWAc>PZj3{`M-rdukL)uWcZnnt{+BXM;xBjL{$ZU4c5f0bzqk8xRiP0r$k9uVu#j;{X5v07*qoM6N<$f(vZM AW&i*H delta 329 zcmV-P0k;0*1LFdaBYy!ZNklu{|0ZI>OnZrgVh8?R6wgx3`pof zVg#Zl8cGj%8S@QJ(*n^>vf1hGdv9K6$4M@dB$ZAtlH*z+N0q9ss^5@FJd;(D=8)Ue z*~#NLmWt==s-Iq}2twotc_)Epw{hbkJ#_WAN%vCl8BdgN#bcFP9VTi5qnGM2;ss4Q3fvvyX@L8hx^qd>+s z2W3NT-~8GR2V-r*eDU@#e;UsUShv}H>tOH%ho;CBc~Yw4)Ix`ZhvxzTdj - {product.name} -
-

{product.description}

-

{product.price}원

-
- favorite-img -

{product.favoriteCount}

-
-
- - ); -} +import BestProductsListItem from "./BestProductsListItem"; function BestProductsList({ products }) { const limitedFavoriteProducts = products.slice(0, 4); return ( -
    +
      {limitedFavoriteProducts.map((product) => { return (
    • diff --git a/src/components/Nav.js b/src/components/Nav.js index af2bb4369..4f9805667 100644 --- a/src/components/Nav.js +++ b/src/components/Nav.js @@ -3,12 +3,6 @@ import Container from "./Container"; import logoImg from "../assets/logo.png"; import styles from "./Nav.module.css"; -function getLinkStyle({ isActive }) { - return { - color: isActive ? "#3692ff" : undefined, - }; -} - function Nav() { return (
      @@ -20,11 +14,18 @@ function Nav() {
    • 자유게시판
    • - + { + return { + color: isActive ? "#3692ff" : "", + }; + }} + >
    • 중고마켓
    - + ); diff --git a/src/components/Nav.module.css b/src/components/Nav.module.css index 4dc6000c6..92276566e 100644 --- a/src/components/Nav.module.css +++ b/src/components/Nav.module.css @@ -4,6 +4,7 @@ padding: 15px 0; background-color: #fff; border-bottom: 1px solid #dfdfdf; + width: 100%; } .container { @@ -12,7 +13,7 @@ justify-content: space-between; } -.loginBtn { +.login__button { padding: 12px 23px 12px 23px; border-radius: 8px; background-color: #3692ff; @@ -23,6 +24,10 @@ line-height: 19.09px; } +.visit__link:focus { + color: #3692ff; +} + .item { font-weight: 700; line-height: 21.48px; diff --git a/src/components/ProductsList.css b/src/components/ProductsList.css index b2d947b79..4d0e661af 100644 --- a/src/components/ProductsList.css +++ b/src/components/ProductsList.css @@ -1,4 +1,4 @@ -.ProductsList { +.Products-list { list-style: none; display: grid; grid-template-columns: repeat(5, 1fr); @@ -31,11 +31,3 @@ h3 { font-size: 14px; line-height: 16.71px; } - -.ProductsListItem-img { - max-width: 100%; - width: 282px; - height: 282px; - border-radius: 16px; - margin-bottom: 16px; -} diff --git a/src/components/ProductsList.js b/src/components/ProductsList.js index ecd228ec0..ea2740ad5 100644 --- a/src/components/ProductsList.js +++ b/src/components/ProductsList.js @@ -1,29 +1,9 @@ import "./ProductsList.css"; -import favoriteImg from "../assets/favorite.png"; - -function ProductsListItem({ product }) { - return ( -
    - {product.name} -
    -

    {product.description}

    -

    {product.price}원

    -
    - favorite-img -

    {product.favoriteCount}

    -
    -
    -
    - ); -} +import ProductsListItem from "./ProductsListItem"; function ProductsList({ products }) { return ( -
      +
        {products.map((product) => { return (
      • diff --git a/src/components/ToggleMenu.js b/src/components/ToggleMenu.js index 88f6260e9..493b3efca 100644 --- a/src/components/ToggleMenu.js +++ b/src/components/ToggleMenu.js @@ -1,10 +1,16 @@ import { useCallback, useEffect, useState } from "react"; import styles from "./ToggleMenu.module.css"; +import arrowDownImg from "../assets/arrow-down.png"; -function ToggleMenu({ onNewestClick, onBestClick }) { +const OPTIONS = { + NEWEST: { label: "최신순", value: "newest" }, + MOST_LIKED: { label: "좋아요순", value: "likes" }, +}; + +function ToggleMenu({ onClick }) { const [isOpen, setIsOpen] = useState(false); - const handleButtonClick = useCallback((e) => { + const onToggleMenu = useCallback((e) => { e.stopPropagation(); setIsOpen((nextIsOpen) => !nextIsOpen); }, []); @@ -22,18 +28,23 @@ function ToggleMenu({ onNewestClick, onBestClick }) { return (
        - {isOpen && (
        • - +
        • - +
        )} diff --git a/src/components/ToggleMenu.module.css b/src/components/ToggleMenu.module.css index 86ff8014b..b05ea05df 100644 --- a/src/components/ToggleMenu.module.css +++ b/src/components/ToggleMenu.module.css @@ -5,11 +5,19 @@ .iconButton { border: 1px solid #ebebeb; cursor: pointer; - padding: 12px 20px 12px 20px; + padding: 12px 30px; border-radius: 12px; gap: 10px; margin: 0; margin-bottom: 5px; + display: flex; + position: relative; + align-items: center; +} + +.toggleMenu .iconText { + position: relative; + left: -15px; } ul.popup { @@ -33,13 +41,12 @@ ul.popup li:hover { } ul.popup li.disabled { - background-color: #fff; + background-color: #ffffff; color: #c4c4c4; user-select: none; cursor: default; border: 1px 1px 0px 1px; height: 42px; - border-radius: 12px 12px 0px 0px; } .toggleMenu ul.popup a:hover { @@ -54,3 +61,9 @@ button { .newestBtn { border-bottom: 1px solid #e5e7eb; } + +.iconButton img { + position: absolute; + right: 12px; + height: 7.42px; +} diff --git a/src/pages/UsedMarketPage.css b/src/pages/UsedMarketPage.css index 20d9408cd..e0b19aea4 100644 --- a/src/pages/UsedMarketPage.css +++ b/src/pages/UsedMarketPage.css @@ -6,8 +6,9 @@ h2 { margin-bottom: 6px; } -div.search-bar { +.search-bar { display: flex; + align-items: center; } .search-container { @@ -18,7 +19,11 @@ div.search-bar { margin-bottom: 24px; } -.search input { +.search-bar__form { + display: flex; +} + +.search-bar__form input { border-radius: 12px; padding: 9px 20px 9px 20px; gap: 10px; @@ -26,11 +31,11 @@ div.search-bar { background-color: #f3f4f6; } -.search input:focus { +.search-bar__form input:focus { outline: none; } -.submit-btn { +.add-button { border-radius: 8px; padding: 12px 23px 12px 23px; gap: 10px; @@ -48,3 +53,14 @@ h2.best-product { margin-top: 24px; margin-bottom: 12px; } + +.search-bar-wrapper { + position: relative; + display: flex; + align-items: center; +} + +.search-bar-wrapper__image { + position: absolute; + left: 5px; +} diff --git a/src/pages/UsedMarketPage.js b/src/pages/UsedMarketPage.js index 1cbf9c90c..e24328602 100644 --- a/src/pages/UsedMarketPage.js +++ b/src/pages/UsedMarketPage.js @@ -1,3 +1,4 @@ +import { Link } from "react-router-dom"; import { useState, useEffect } from "react"; import { getProducts } from "../api"; import ProductsList from "../components/ProductsList"; @@ -11,10 +12,9 @@ function UsedMarketPage() { const [order, setOrder] = useState("createdAt"); const [favoriteProducts, setFavoriteProducts] = useState([]); - const sortedProducts = products.sort((a, b) => b[order] - a[order]); + const [searchText, setSearchText] = useState(""); - const handleNewestClick = () => setOrder("createdAt"); - const handleBestClick = () => setOrder("favoriteCount"); + const sortedProducts = products.sort((a, b) => b[order] - a[order]); const handleLoad = async () => { const { list } = await getProducts(); @@ -32,6 +32,14 @@ function UsedMarketPage() { setFavoriteProducts(sortedFavoriteProducts); }, [products]); + const handleToggleClick = (option) => { + if (option === "newest") { + setOrder("createdAt"); + } else if (option === "likes") { + setOrder("favoriteCount"); + } + }; + return (
        @@ -41,20 +49,28 @@ function UsedMarketPage() {

        전체 상품

        -
        - 검색 - - +
        - +
        From cdbff5d103d94f617be7e0124a81f4ea6ce8d12d Mon Sep 17 00:00:00 2001 From: baehyunji Date: Fri, 26 Apr 2024 23:32:07 +0900 Subject: [PATCH 05/10] =?UTF-8?q?feat:=20=EC=8A=A4=ED=94=84=EB=A6=B0?= =?UTF-8?q?=ED=8A=B8=20=EB=AF=B8=EC=85=986?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/arrow-down.png | Bin 0 -> 273 bytes src/assets/plus-icon.png | Bin 0 -> 318 bytes src/components/BestProductsListItem.css | 11 +++ src/components/BestProductsListItem.js | 24 ++++++ src/components/FileInput.css | 54 +++++++++++++ src/components/FileInput.js | 58 ++++++++++++++ src/components/ProductRagistrationForm.css | 79 ++++++++++++++++++ src/components/ProductRagistrationForm.js | 89 +++++++++++++++++++++ src/components/ProductsListItem.css | 7 ++ src/components/ProductsListItem.js | 24 ++++++ src/pages/ProductRagistrationPage.js | 7 ++ 11 files changed, 353 insertions(+) create mode 100644 src/assets/arrow-down.png create mode 100644 src/assets/plus-icon.png create mode 100644 src/components/BestProductsListItem.css create mode 100644 src/components/BestProductsListItem.js create mode 100644 src/components/FileInput.css create mode 100644 src/components/FileInput.js create mode 100644 src/components/ProductRagistrationForm.css create mode 100644 src/components/ProductRagistrationForm.js create mode 100644 src/components/ProductsListItem.css create mode 100644 src/components/ProductsListItem.js create mode 100644 src/pages/ProductRagistrationPage.js diff --git a/src/assets/arrow-down.png b/src/assets/arrow-down.png new file mode 100644 index 0000000000000000000000000000000000000000..771f146a2e9b5e85a76c272c789f00a1b1aa8710 GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^d_c^>!3HEx3Z6Ryq&N#aB8wRqxP?KOkzv*x37{Zj zage(c!@6@aFM%AEbVpxD28NCO+dk4sW2Hfsi}nMG_6_0tv$=c_r>`YGrxc0^^)wd`}1%xP?RJ*}@*RB-3psY{Q4Ea}^Cn&FgT_$2#Kh{~_Z zaNZwH$8LXk`=@|?>TIbu0Vmd!2nKx36^-7&p2!v`d9^R**T$HiyX*go=O$Ye&F9xp Q0=k&N)78&qol`;+0I!N*a{vGU literal 0 HcmV?d00001 diff --git a/src/assets/plus-icon.png b/src/assets/plus-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..df2fb44817b1868405389a10ed8facd6a2406d8b GIT binary patch literal 318 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?3oVGw3ym^DWND9BhG zM^%~R1P zar<}IPOoODVDuC^UL*bK@FQ6k7IB6AJyLv3Oxzc^nIn=JvUw7E8P^y#m~QQz7RLF; zINow*RMeVZY+kG1tlfC@72~9X&rUxQ+M=1oF#XAd^`5`DfBv@1nw+4%>GRu(uRhjo zZd!Bn{t8pJ4aXR+NgR*@n%E$3=PMM!mdLnD?DiF{&W?_MADL#!o9k>`vHm8|dkmhg KelF{r5}E+9WON_^ literal 0 HcmV?d00001 diff --git a/src/components/BestProductsListItem.css b/src/components/BestProductsListItem.css new file mode 100644 index 000000000..53213f430 --- /dev/null +++ b/src/components/BestProductsListItem.css @@ -0,0 +1,11 @@ +.best-products-list-item__img { + max-width: 100%; + width: 282px; + height: 282px; + border-radius: 16px; + margin-bottom: 16px; +} + +.favorite-contents h3 { + color: #4b5563; +} diff --git a/src/components/BestProductsListItem.js b/src/components/BestProductsListItem.js new file mode 100644 index 000000000..b802c4fbb --- /dev/null +++ b/src/components/BestProductsListItem.js @@ -0,0 +1,24 @@ +import "./BestProductsListItem.css"; +import favoriteImg from "../assets/favorite.png"; + +function BestProductsListItem({ product }) { + return ( +
        + {product.name} +
        +

        {product.description}

        +

        {product.price}원

        +
        + favorite-img +

        {product.favoriteCount}

        +
        +
        +
        + ); +} + +export default BestProductsListItem; diff --git a/src/components/FileInput.css b/src/components/FileInput.css new file mode 100644 index 000000000..ee62bc71d --- /dev/null +++ b/src/components/FileInput.css @@ -0,0 +1,54 @@ +.image-upload { + display: flex; +} + +.image-upload-frame { + width: 282px; + height: 315px; + margin-bottom: 24px; + margin-right: 24px; + border-radius: 12px; + background-color: #f3f4f6; + cursor: pointer; + display: flex; +} + +.image-upload-frame-wrapper { + display: flex; + flex-direction: column; + align-items: center; + margin: 99px auto; +} + +image-upload-frame-wrapper img { + width: 48px; + height: 48px; +} + +image-upload-frame-wrapper p { + color: #9ca3af; + font-weight: 600; +} + +img.image-upload__image { + width: 282px; + height: 315px; + border-radius: 12px; + border: 1px; +} + +.image-delete-button { + background-color: #3692ff; + color: #ffffff; + border-radius: 50%; + width: 20px; + height: 20px; +} + +input#file-input { + display: none; +} + +.image-upload__p { + color: #9ca3af; +} diff --git a/src/components/FileInput.js b/src/components/FileInput.js new file mode 100644 index 000000000..6612d04f8 --- /dev/null +++ b/src/components/FileInput.js @@ -0,0 +1,58 @@ +import { useRef } from "react"; +import { useEffect, useState } from "react"; +import iconImg from "../assets/plus-icon.png"; +import "./FileInput.css"; + +function FileInput({ name, value, onChange }) { + const [preview, setPreview] = useState(); + const inputRef = useRef(); + + const handleChange = (e) => { + const nextValue = e.target.files[0]; + onChange(name, nextValue); + }; + + useEffect(() => { + if (!value) { + setPreview(null); + return; + } + const nextPreview = URL.createObjectURL(value); + setPreview(nextPreview); + }, [value]); + + const handleClearClick = () => { + const inputNode = inputRef.current; + if (!inputNode) return; + + inputNode.value = ""; + onChange(name, null); + }; + + return ( +
        +
        + + +
        + {preview && ( + 이미지 + )} + {value && ( + + )} +
        + ); +} + +export default FileInput; diff --git a/src/components/ProductRagistrationForm.css b/src/components/ProductRagistrationForm.css new file mode 100644 index 000000000..b4ed0c7b9 --- /dev/null +++ b/src/components/ProductRagistrationForm.css @@ -0,0 +1,79 @@ +form.product-ragistration-form { + display: flex; + flex-direction: column; + margin: 24px auto 0px; +} + +form.product-ragistration-form h1 { + font-size: 28px; + font-weight: 700; + line-height: 33.41px; +} + +textarea { + width: 1200px; + height: 200px; + border: none; +} + +form.product-ragistration-form label { + font-size: 18px; + font-weight: 700; + line-height: 21.48px; + margin-bottom: 12px; +} + +form.product-ragistration-form input { + gap: 10px; + border: none; + height: 56px; +} + +.product-ragistration-form input:focus, +textarea:focus { + outline: none; +} + +form.product-ragistration-form input, +textarea { + font-size: 16px; + font-weight: 400; + line-height: 24px; + border-radius: 12px; + background-color: #f3f4f6; + margin-bottom: 24px; +} + +.product-ragistration-form-wrapper { + display: flex; + justify-content: space-between; + align-items: center; +} + +.product-ragistration-form-wrapper button { + background-color: #9ca3af; + color: #ffffff; + padding: 12px 20px 12px 20px; + border-radius: 8px; + gap: 10px; + width: 88px; + height: 42px; + font-size: 16px; + font-weight: 600; +} + +form:valid .product-ragistration-form__button { + background-color: #3692ff; +} + +.image-upload-frame-wrapper input::placeholder { + margin: 2px 0 0 2px; +} + +.product-ragistration-form input { + padding: 12px 0 12px 24px; +} + +textarea#product-description { + padding: 12px 0 12px 24px; +} diff --git a/src/components/ProductRagistrationForm.js b/src/components/ProductRagistrationForm.js new file mode 100644 index 000000000..476073026 --- /dev/null +++ b/src/components/ProductRagistrationForm.js @@ -0,0 +1,89 @@ +import { useState } from "react"; +import FileInput from "./FileInput"; +import "./ProductRagistrationForm.css"; + +function ProductRagistrationForm() { + const [values, setValues] = useState({ + imageFile: null, + productName: "", + productDescription: "", + productPrice: "", + productTag: "", + }); + + const handleChange = (name, value) => { + setValues((prevValues) => ({ + ...prevValues, + [name]: value, + })); + }; + + const handleInputChange = (e) => { + const { name, value } = e.target; + handleChange(name, value); + }; + + const handleSubmit = (e) => { + e.preventDefault(); + console.log(values); + }; + + return ( +
        +
        +

        상품등록하기

        + +
        + + + + + + + + + + +
        + ); +} + +export default ProductRagistrationForm; diff --git a/src/components/ProductsListItem.css b/src/components/ProductsListItem.css new file mode 100644 index 000000000..3d4f1cfe9 --- /dev/null +++ b/src/components/ProductsListItem.css @@ -0,0 +1,7 @@ +.products-list-item__img { + max-width: 100%; + width: 282px; + height: 282px; + border-radius: 16px; + margin-bottom: 16px; +} diff --git a/src/components/ProductsListItem.js b/src/components/ProductsListItem.js new file mode 100644 index 000000000..2c5fe40f2 --- /dev/null +++ b/src/components/ProductsListItem.js @@ -0,0 +1,24 @@ +import "./ProductsListItem.css"; +import favoriteImg from "../assets/favorite.png"; + +function ProductsListItem({ product }) { + return ( +
        + {product.name} +
        +

        {product.description}

        +

        {product.price}원

        +
        + favorite-img +

        {product.favoriteCount}

        +
        +
        +
        + ); +} + +export default ProductsListItem; diff --git a/src/pages/ProductRagistrationPage.js b/src/pages/ProductRagistrationPage.js new file mode 100644 index 000000000..ea00b9c8d --- /dev/null +++ b/src/pages/ProductRagistrationPage.js @@ -0,0 +1,7 @@ +import ProductRagistrationForm from "../components/ProductRagistrationForm"; + +function ProductRagistrationPage() { + return ; +} + +export default ProductRagistrationPage; From 3ba35284e6d8d6a2fd2d7816b177e77afffb1175 Mon Sep 17 00:00:00 2001 From: baehyunji Date: Fri, 31 May 2024 11:41:37 +0900 Subject: [PATCH 06/10] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 35 ++++++- package.json | 1 + src/Main.js | 6 +- src/api.js | 15 +-- src/assets/arrow-down.png | Bin 273 -> 298 bytes src/assets/arrow-left.png | Bin 0 -> 302 bytes src/assets/arrow-right.png | Bin 0 -> 290 bytes src/assets/userIcon.png | Bin 0 -> 1258 bytes src/components/BestProductsListItem.css | 11 -- src/components/FileInput.js | 58 ----------- src/components/Nav.js | 34 ------- src/components/ProductRagistrationForm.css | 79 -------------- src/components/ProductsListItem.css | 7 -- src/components/ToggleMenu.js | 55 ---------- .../{Container.js => container/Container.jsx} | 0 .../{ => container}/Container.module.css | 0 src/components/container/ListContainer.jsx | 56 ++++++++++ .../container/PaginationContainer.jsx | 8 ++ .../container/RagistrationContainer.jsx | 47 +++++++++ .../container/ToggleMenuContainer.jsx | 27 +++++ src/components/{App.js => ui/App.jsx} | 0 src/components/{ => ui}/App.module.css | 26 ++++- .../BestProduct.jsx} | 14 +-- src/components/ui/BestProduct.module.css | 15 +++ .../BestProductsList.jsx} | 9 +- .../BestProductsList.module.css} | 2 +- src/components/ui/Button.jsx | 15 +++ src/components/ui/Button.module.css | 31 ++++++ src/components/ui/FileInput.jsx | 67 ++++++++++++ .../FileInput.module.css} | 25 +++-- src/components/ui/Logo.jsx | 15 +++ src/components/ui/Logo.module.css | 11 ++ src/components/ui/Nav.jsx | 55 ++++++++++ src/components/{ => ui}/Nav.module.css | 28 ++--- src/components/ui/PaginationBar.jsx | 23 +++++ src/components/ui/PaginationBar.module.css | 24 +++++ .../{ProductsListItem.js => ui/Product.jsx} | 12 +-- src/components/ui/Product.module.css | 15 +++ src/components/ui/ProductListContainer.jsx | 50 +++++++++ .../ui/ProductListContainer.module.css | 48 +++++++++ .../{ProductsList.js => ui/ProductsList.jsx} | 9 +- .../ProductsList.module.css} | 15 +-- .../RagistrationForm.jsx} | 51 ++++------ src/components/ui/RagistrationForm.module.css | 96 ++++++++++++++++++ src/components/ui/ToggleMenu.jsx | 38 +++++++ src/components/{ => ui}/ToggleMenu.module.css | 16 +-- src/components/ui/global.css | 16 +++ src/pages/FreeBoardPage.js | 9 ++ src/pages/ProductRagistrationPage.js | 4 +- src/pages/UsedMarketPage.css | 66 ------------ src/pages/UsedMarketPage.js | 80 +-------------- 51 files changed, 814 insertions(+), 510 deletions(-) create mode 100644 src/assets/arrow-left.png create mode 100644 src/assets/arrow-right.png create mode 100644 src/assets/userIcon.png delete mode 100644 src/components/BestProductsListItem.css delete mode 100644 src/components/FileInput.js delete mode 100644 src/components/Nav.js delete mode 100644 src/components/ProductRagistrationForm.css delete mode 100644 src/components/ProductsListItem.css delete mode 100644 src/components/ToggleMenu.js rename src/components/{Container.js => container/Container.jsx} (100%) rename src/components/{ => container}/Container.module.css (100%) create mode 100644 src/components/container/ListContainer.jsx create mode 100644 src/components/container/PaginationContainer.jsx create mode 100644 src/components/container/RagistrationContainer.jsx create mode 100644 src/components/container/ToggleMenuContainer.jsx rename src/components/{App.js => ui/App.jsx} (100%) rename src/components/{ => ui}/App.module.css (54%) rename src/components/{BestProductsListItem.js => ui/BestProduct.jsx} (51%) create mode 100644 src/components/ui/BestProduct.module.css rename src/components/{BestProductsList.js => ui/BestProductsList.jsx} (58%) rename src/components/{BestProductsList.css => ui/BestProductsList.module.css} (70%) create mode 100644 src/components/ui/Button.jsx create mode 100644 src/components/ui/Button.module.css create mode 100644 src/components/ui/FileInput.jsx rename src/components/{FileInput.css => ui/FileInput.module.css} (65%) create mode 100644 src/components/ui/Logo.jsx create mode 100644 src/components/ui/Logo.module.css create mode 100644 src/components/ui/Nav.jsx rename src/components/{ => ui}/Nav.module.css (60%) create mode 100644 src/components/ui/PaginationBar.jsx create mode 100644 src/components/ui/PaginationBar.module.css rename src/components/{ProductsListItem.js => ui/Product.jsx} (58%) create mode 100644 src/components/ui/Product.module.css create mode 100644 src/components/ui/ProductListContainer.jsx create mode 100644 src/components/ui/ProductListContainer.module.css rename src/components/{ProductsList.js => ui/ProductsList.jsx} (53%) rename src/components/{ProductsList.css => ui/ProductsList.module.css} (61%) rename src/components/{ProductRagistrationForm.js => ui/RagistrationForm.jsx} (66%) create mode 100644 src/components/ui/RagistrationForm.module.css create mode 100644 src/components/ui/ToggleMenu.jsx rename src/components/{ => ui}/ToggleMenu.module.css (79%) create mode 100644 src/components/ui/global.css create mode 100644 src/pages/FreeBoardPage.js delete mode 100644 src/pages/UsedMarketPage.css diff --git a/package-lock.json b/package-lock.json index 8b6c3e46f..772e4d846 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.7.2", "classnames": "^2.5.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -5308,6 +5309,29 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -8322,9 +8346,9 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -14403,6 +14427,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", diff --git a/package.json b/package.json index ea7b18a19..7a3786a45 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.7.2", "classnames": "^2.5.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/Main.js b/src/Main.js index 056dfd213..b0bda43a5 100644 --- a/src/Main.js +++ b/src/Main.js @@ -1,13 +1,17 @@ import { BrowserRouter, Routes, Route } from "react-router-dom"; -import App from "./components/App"; +import App from "./components/ui/App"; import UsedMarketPage from "./pages/UsedMarketPage"; +import ProductRagistrationPage from "./pages/ProductRagistrationPage"; +import FreeBoardPage from "./pages/FreeBoardPage"; function Main() { return ( + } /> } /> + } /> diff --git a/src/api.js b/src/api.js index 0d279986a..436f1d354 100644 --- a/src/api.js +++ b/src/api.js @@ -1,8 +1,11 @@ +import axios from "axios"; + +const instance = axios.create({ + baseURL: "https://panda-market-api.vercel.app", + timeout: 3000, +}); + export async function getProducts() { - const response = await fetch("https://panda-market-api.vercel.app/products/"); - if (!response.ok) { - throw new Error("데이터를 불러오는데 실패했습니다"); - } - const body = await response.json(); - return body; + const res = await instance.get("/products/"); + return res.data; } diff --git a/src/assets/arrow-down.png b/src/assets/arrow-down.png index 771f146a2e9b5e85a76c272c789f00a1b1aa8710..0e312dd6d4d2c9bbbbbadd69f72e3cb3580183a5 100644 GIT binary patch delta 255 zcmVmzsf4@mY zK~#7F?a(0(!Y~wo;nxnqFn|Cr&Bwm^W^&<(>17lPNKQ$Tz0Rpv0B&h-jYa12KZWv2C51Dd$k)Dbx&8E@G2bo)2M z8gNAP@Qg7kZ2z4BQ$&Hu{X!@ZGZQj%4&g&+vLyaD&nKhjf7NAdsw002ovPDHLk FV1jGtW=8-3 delta 230 zcmV7zG`MZDp7j0uWa-R<``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eB{$WoS$B+ufrxSJgm<$En(#wnsdQBxbI5l*% z1r~TUu$=67EMk5jmi<9u(xM40{+l?QJPd4^>YTl`3*H9l*j!#V_j~Fx)*t*)&4Fi2 z#SgGs{XMyeA@SBb-<#JN-Q12Wz1Q_<^0{qNUnXiUp7GhGg8j!Sw$~g{d9y+nJ*wPf z+~6ej`ku>zg1lWPxjAy%x1W9)xYDdlNX6M9OzZl?I^X_nQO}j7ZWM^68d*0qd3e2e uw>_KBPI=G6*)9PN8-uc*{yHZAfc3MOg95L==n9~B7(8A5T-G@yGywowy==Sy literal 0 HcmV?d00001 diff --git a/src/assets/arrow-right.png b/src/assets/arrow-right.png new file mode 100644 index 0000000000000000000000000000000000000000..c8fe6f123a71a9fc19f7b4b4410a2a664b868fc8 GIT binary patch literal 290 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eB{&r6n$B+ufyZyFYhYTdz_RCxE{KCQNDDHTW z+oJ5uiyMn?R7b2>{Xkx$udpOY-1Um{7Y0+qyOYg|=c_(^T038$q0C6sQ=(ht)5}S^ zZ`gD+6<#WD#=L7c&s{sb%P{NnCQ13QZcB?7H=O=`ye89|yqrOF iiP!57J5Cm5*w<%&wVdy?P9YTN1qM%7KbLh*2~7ZkW^S4Q literal 0 HcmV?d00001 diff --git a/src/assets/userIcon.png b/src/assets/userIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..634ea26db0f7f98ec6db135ef5a302aaf8661e28 GIT binary patch literal 1258 zcmV{kP^0obdaP1n+ldxu$0lWdmc7cg92q-&)493ES9**^FVSfSJ z?!10o>%bbYB$K<{ZduC}-s7qd3o+7&9=>%AZk{m!2ap*-}I~&B7i}A`(^cG=y=eNbflSF;WG|4 z;VBSsWKPw$jfOGy5AmS+S~h?9Wa0=0yV;!eIcR7_ARVidHl7%P263+KW9f)b250XW zu>U{;hR&1E>yXtlFh_5{zPZCs+ZogFxBP|ke&iH%!WXH$D zCRgDwZUI@Yy7l;K=LVC&5*3FH)>{tTZpKZdlwy=OAq~j-<&xZ9Ot4+DcWhFoe7DZA zhN0&)CmqEh1Z#4x@IT`wZlLd_jm(}H8-JN zkxX4k-q#qA|4_$bKO|f33jht1`itikCrH$g%0b6Ijp=agx&0OQ=AEcXy zyklxaS@XI;1V>yjIEtHfF}{k9wuDdypawS#ko{3#_4 zSnpNsS;IL4dk&xCrc+ygF}i~B_SqULx{(jSkZ=7&=M;6j|mPhbd!a=9y*TMqUBm|Ng%PaU2O*)wLeimmazRW z%0KxZCI#BMKNud0hesdedoQXLNSl@a%J_Wd)pqr7|GdCdKh3HCz(WG3$o(C7+c>n` z|Mc((l5)~NZ(m*Cir>NAWGcQ5hT^1uCY9nsF-`Lw?$;G5gB|yX9*#m9;C^*{9f { - const nextValue = e.target.files[0]; - onChange(name, nextValue); - }; - - useEffect(() => { - if (!value) { - setPreview(null); - return; - } - const nextPreview = URL.createObjectURL(value); - setPreview(nextPreview); - }, [value]); - - const handleClearClick = () => { - const inputNode = inputRef.current; - if (!inputNode) return; - - inputNode.value = ""; - onChange(name, null); - }; - - return ( -
        -
        - - -
        - {preview && ( - 이미지 - )} - {value && ( - - )} -
        - ); -} - -export default FileInput; diff --git a/src/components/Nav.js b/src/components/Nav.js deleted file mode 100644 index 4f9805667..000000000 --- a/src/components/Nav.js +++ /dev/null @@ -1,34 +0,0 @@ -import { Link, NavLink } from "react-router-dom"; -import Container from "./Container"; -import logoImg from "../assets/logo.png"; -import styles from "./Nav.module.css"; - -function Nav() { - return ( -
        - -
          - - Codethat Logo - - -
        • 자유게시판
        • -
          - { - return { - color: isActive ? "#3692ff" : "", - }; - }} - > -
        • 중고마켓
        • -
          -
        - -
        -
        - ); -} - -export default Nav; diff --git a/src/components/ProductRagistrationForm.css b/src/components/ProductRagistrationForm.css deleted file mode 100644 index b4ed0c7b9..000000000 --- a/src/components/ProductRagistrationForm.css +++ /dev/null @@ -1,79 +0,0 @@ -form.product-ragistration-form { - display: flex; - flex-direction: column; - margin: 24px auto 0px; -} - -form.product-ragistration-form h1 { - font-size: 28px; - font-weight: 700; - line-height: 33.41px; -} - -textarea { - width: 1200px; - height: 200px; - border: none; -} - -form.product-ragistration-form label { - font-size: 18px; - font-weight: 700; - line-height: 21.48px; - margin-bottom: 12px; -} - -form.product-ragistration-form input { - gap: 10px; - border: none; - height: 56px; -} - -.product-ragistration-form input:focus, -textarea:focus { - outline: none; -} - -form.product-ragistration-form input, -textarea { - font-size: 16px; - font-weight: 400; - line-height: 24px; - border-radius: 12px; - background-color: #f3f4f6; - margin-bottom: 24px; -} - -.product-ragistration-form-wrapper { - display: flex; - justify-content: space-between; - align-items: center; -} - -.product-ragistration-form-wrapper button { - background-color: #9ca3af; - color: #ffffff; - padding: 12px 20px 12px 20px; - border-radius: 8px; - gap: 10px; - width: 88px; - height: 42px; - font-size: 16px; - font-weight: 600; -} - -form:valid .product-ragistration-form__button { - background-color: #3692ff; -} - -.image-upload-frame-wrapper input::placeholder { - margin: 2px 0 0 2px; -} - -.product-ragistration-form input { - padding: 12px 0 12px 24px; -} - -textarea#product-description { - padding: 12px 0 12px 24px; -} diff --git a/src/components/ProductsListItem.css b/src/components/ProductsListItem.css deleted file mode 100644 index 3d4f1cfe9..000000000 --- a/src/components/ProductsListItem.css +++ /dev/null @@ -1,7 +0,0 @@ -.products-list-item__img { - max-width: 100%; - width: 282px; - height: 282px; - border-radius: 16px; - margin-bottom: 16px; -} diff --git a/src/components/ToggleMenu.js b/src/components/ToggleMenu.js deleted file mode 100644 index 493b3efca..000000000 --- a/src/components/ToggleMenu.js +++ /dev/null @@ -1,55 +0,0 @@ -import { useCallback, useEffect, useState } from "react"; -import styles from "./ToggleMenu.module.css"; -import arrowDownImg from "../assets/arrow-down.png"; - -const OPTIONS = { - NEWEST: { label: "최신순", value: "newest" }, - MOST_LIKED: { label: "좋아요순", value: "likes" }, -}; - -function ToggleMenu({ onClick }) { - const [isOpen, setIsOpen] = useState(false); - - const onToggleMenu = useCallback((e) => { - e.stopPropagation(); - setIsOpen((nextIsOpen) => !nextIsOpen); - }, []); - - useEffect(() => { - if (!isOpen) return; - - const handleClickOutside = () => setIsOpen(false); - window.addEventListener("click", handleClickOutside); - - return () => { - window.removeEventListener("click", handleClickOutside); - }; - }, [isOpen]); - - return ( -
        - - {isOpen && ( -
          -
          -
        • - -
        • -
          -
        • - -
        • -
        - )} -
        - ); -} - -export default ToggleMenu; diff --git a/src/components/Container.js b/src/components/container/Container.jsx similarity index 100% rename from src/components/Container.js rename to src/components/container/Container.jsx diff --git a/src/components/Container.module.css b/src/components/container/Container.module.css similarity index 100% rename from src/components/Container.module.css rename to src/components/container/Container.module.css diff --git a/src/components/container/ListContainer.jsx b/src/components/container/ListContainer.jsx new file mode 100644 index 000000000..43a68d96d --- /dev/null +++ b/src/components/container/ListContainer.jsx @@ -0,0 +1,56 @@ +import ProductListContainer from "../ui/ProductListContainer"; +import { useState, useEffect } from "react"; +import { getProducts } from "../../api"; + +function ListContainer() { + const [products, setProducts] = useState([]); + const [order, setOrder] = useState("createdAt"); + const [favoriteProducts, setFavoriteProducts] = useState([]); + const [searchText, setSearchText] = useState(""); + + const sortedProducts = products.sort((a, b) => b[order] - a[order]); + + const handleLoad = async () => { + try { + const { list } = await getProducts(); + setProducts(list); + } catch (e) { + if (e.response) { + console.log(e.response.status); + console.log(e.response.data); + } else { + console.log("리퀘스트가 실패했습니다."); + } + } + }; + + useEffect(() => { + handleLoad(); + }, [order]); + + useEffect(() => { + const sortedFavoriteProducts = [...products].sort( + (a, b) => b.favoriteCount - a.favoriteCount + ); + setFavoriteProducts(sortedFavoriteProducts); + }, [products]); + + const handleToggleClick = (option) => { + if (option === "newest") { + setOrder("createdAt"); + } else if (option === "likes") { + setOrder("favoriteCount"); + } + }; + return ( + + ); +} + +export default ListContainer; diff --git a/src/components/container/PaginationContainer.jsx b/src/components/container/PaginationContainer.jsx new file mode 100644 index 000000000..ad43a6d15 --- /dev/null +++ b/src/components/container/PaginationContainer.jsx @@ -0,0 +1,8 @@ +// import { getProducts } from "../../api"; +// import PaginationBar from "../ui/PaginationBar"; + +function PaginationContainer() { + return; +} + +export default PaginationContainer; diff --git a/src/components/container/RagistrationContainer.jsx b/src/components/container/RagistrationContainer.jsx new file mode 100644 index 000000000..012ba65b0 --- /dev/null +++ b/src/components/container/RagistrationContainer.jsx @@ -0,0 +1,47 @@ +import RagistrationForm from "../ui/RagistrationForm"; +import { useState } from "react"; + +function RagistrationContainer() { + const [values, setValues] = useState({ + imageFile: null, + productName: "", + productDescription: "", + productPrice: "", + productTag: "", + }); + + const handleChange = (name, value) => { + setValues((prevValues) => ({ + ...prevValues, + [name]: value, + })); + }; + + const handleInputChange = (e) => { + const { name, value } = e.target; + handleChange(name, value); + }; + + const handleSubmit = (e) => { + e.preventDefault(); + console.log(values); + + setValues({ + imageFile: null, + productName: "", + productDescription: "", + productPrice: "", + productTag: "", + }); + }; + return ( + + ); +} + +export default RagistrationContainer; diff --git a/src/components/container/ToggleMenuContainer.jsx b/src/components/container/ToggleMenuContainer.jsx new file mode 100644 index 000000000..82285ef5a --- /dev/null +++ b/src/components/container/ToggleMenuContainer.jsx @@ -0,0 +1,27 @@ +import { useCallback, useEffect, useState } from "react"; +import ToggleMenu from "../ui/ToggleMenu"; + +function ToggleMenuContainer({ onClick }) { + const [isOpen, setIsOpen] = useState(false); + + const onToggleMenu = useCallback((e) => { + e.stopPropagation(); + setIsOpen((nextIsOpen) => !nextIsOpen); + }, []); + + useEffect(() => { + if (!isOpen) return; + + const handleClickOutside = () => setIsOpen(false); + window.addEventListener("click", handleClickOutside); + + return () => { + window.removeEventListener("click", handleClickOutside); + }; + }, [isOpen]); + return ( + + ); +} + +export default ToggleMenuContainer; diff --git a/src/components/App.js b/src/components/ui/App.jsx similarity index 100% rename from src/components/App.js rename to src/components/ui/App.jsx diff --git a/src/components/App.module.css b/src/components/ui/App.module.css similarity index 54% rename from src/components/App.module.css rename to src/components/ui/App.module.css index 65964649f..401c79b8f 100644 --- a/src/components/App.module.css +++ b/src/components/ui/App.module.css @@ -1,14 +1,17 @@ -@import-normalize; /* bring in normalize.css styles */ +@import-normalize; * { box-sizing: border-box; word-break: keep-all; font-family: "Pretendard"; + font-size: 16px; + font-weight: 400; + color: #1f2937; } html { overflow-y: scroll; - background-color: #f9f9f9; + background-color: #fcfcfc; } body { @@ -16,8 +19,8 @@ body { } a { - color: #494949; text-decoration: none; + color: #4b5563; } :global(#root) { @@ -33,3 +36,20 @@ a { width: 100%; margin: 0 auto; } + +button { + border: none; + cursor: pointer; +} + +@media (max-width: 1199px) { + .body { + padding: 0 24px; + } +} + +@media (max-width: 767px) { + .body { + padding: 0 16px; + } +} diff --git a/src/components/BestProductsListItem.js b/src/components/ui/BestProduct.jsx similarity index 51% rename from src/components/BestProductsListItem.js rename to src/components/ui/BestProduct.jsx index b802c4fbb..cf3cbd1e2 100644 --- a/src/components/BestProductsListItem.js +++ b/src/components/ui/BestProduct.jsx @@ -1,16 +1,16 @@ -import "./BestProductsListItem.css"; -import favoriteImg from "../assets/favorite.png"; +import styles from "./BestProduct.module.css"; +import favoriteImg from "../../assets/favorite.png"; -function BestProductsListItem({ product }) { +function BestProduct({ product }) { return ( -
        +
        {product.name}
        -

        {product.description}

        +

        {product.description}

        {product.price}원

        favorite-img @@ -21,4 +21,4 @@ function BestProductsListItem({ product }) { ); } -export default BestProductsListItem; +export default BestProduct; diff --git a/src/components/ui/BestProduct.module.css b/src/components/ui/BestProduct.module.css new file mode 100644 index 000000000..60dd388a0 --- /dev/null +++ b/src/components/ui/BestProduct.module.css @@ -0,0 +1,15 @@ +.bestItem__img { + max-width: 100%; + width: 282px; + height: 282px; + border-radius: 16px; + margin-bottom: 16px; +} + +.best_description { + display: inline-block; + width: 180px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/src/components/BestProductsList.js b/src/components/ui/BestProductsList.jsx similarity index 58% rename from src/components/BestProductsList.js rename to src/components/ui/BestProductsList.jsx index 7dbc686ef..cb697d771 100644 --- a/src/components/BestProductsList.js +++ b/src/components/ui/BestProductsList.jsx @@ -1,14 +1,15 @@ -import "./BestProductsList.css"; -import BestProductsListItem from "./BestProductsListItem"; +import styles from "./BestProductsList.module.css"; +import "./global.css"; +import BestProduct from "./BestProduct"; function BestProductsList({ products }) { const limitedFavoriteProducts = products.slice(0, 4); return ( -
          +
            {limitedFavoriteProducts.map((product) => { return (
          • - +
          • ); })} diff --git a/src/components/BestProductsList.css b/src/components/ui/BestProductsList.module.css similarity index 70% rename from src/components/BestProductsList.css rename to src/components/ui/BestProductsList.module.css index 4a2f552ca..70d099416 100644 --- a/src/components/BestProductsList.css +++ b/src/components/ui/BestProductsList.module.css @@ -1,4 +1,4 @@ -.best-products-list { +.bestProductsList { list-style: none; display: flex; padding: 0; diff --git a/src/components/ui/Button.jsx b/src/components/ui/Button.jsx new file mode 100644 index 000000000..eb01c8b95 --- /dev/null +++ b/src/components/ui/Button.jsx @@ -0,0 +1,15 @@ +import styles from "./Button.module.css"; + +function Button({ onClick, children, variant, type }) { + const buttonClassName = `${styles.button} ${styles[variant] || ""}`; + + return ( + <> + + + ); +} + +export default Button; diff --git a/src/components/ui/Button.module.css b/src/components/ui/Button.module.css new file mode 100644 index 000000000..94ec2b1be --- /dev/null +++ b/src/components/ui/Button.module.css @@ -0,0 +1,31 @@ +.button { + height: 42px; + width: 88px; + border-radius: 8px; + padding: 12px 23px 12px 23px; + gap: 10px; + background-color: #3692ff; + color: #ffffff; + border: none; + font-size: 16px; + font-weight: 600; + line-height: 19.09px; + cursor: pointer; +} + +.login { + margin-top: 14px; + margin-bottom: 14px; +} + +.productRagistration { + width: 133px; + margin-left: 12px; + margin-right: 12px; +} +/* Tablet size*/ +@media screen and (max-width: 1199px) { +} +/*Mobile size*/ +@media screen and (max-width: 767px) { +} diff --git a/src/components/ui/FileInput.jsx b/src/components/ui/FileInput.jsx new file mode 100644 index 000000000..e0f8236e6 --- /dev/null +++ b/src/components/ui/FileInput.jsx @@ -0,0 +1,67 @@ +import { useRef } from "react"; +import iconImg from "../../assets/plus-icon.png"; +import styles from "./FileInput.module.css"; + +function FileInput({ name, value, onChange }) { + const inputRef = useRef(); + + let preview = null; + + const handleChange = (e) => { + const nextValue = e.target.files[0]; + onChange(name, nextValue); + }; + + const handleClearClick = () => { + const inputNode = inputRef.current; + if (!inputNode) return; + + inputNode.value = ""; + onChange(name, null); + }; + + return ( +
            +
            + + +
            + {value && ( + 이미지 + )} + {preview && ( + 이미지 + )} + {value && ( + + )} +
            + ); +} + +export default FileInput; diff --git a/src/components/FileInput.css b/src/components/ui/FileInput.module.css similarity index 65% rename from src/components/FileInput.css rename to src/components/ui/FileInput.module.css index ee62bc71d..3ffe34949 100644 --- a/src/components/FileInput.css +++ b/src/components/ui/FileInput.module.css @@ -1,8 +1,8 @@ -.image-upload { +.image_upload { display: flex; } -.image-upload-frame { +.image_upload_frame { width: 282px; height: 315px; margin-bottom: 24px; @@ -10,45 +10,50 @@ border-radius: 12px; background-color: #f3f4f6; cursor: pointer; - display: flex; } -.image-upload-frame-wrapper { +.image_upload_frame_wrapper { display: flex; flex-direction: column; align-items: center; margin: 99px auto; } -image-upload-frame-wrapper img { +.image_upload_frame_wrapper img { width: 48px; height: 48px; } -image-upload-frame-wrapper p { +.image_upload_frame_wrapper p { color: #9ca3af; font-weight: 600; } -img.image-upload__image { +img.image_upload__image { width: 282px; height: 315px; border-radius: 12px; border: 1px; } -.image-delete-button { +.image_delete_button { background-color: #3692ff; color: #ffffff; border-radius: 50%; width: 20px; height: 20px; + margin-left: -30px; + margin-top: 10px; } -input#file-input { +.file_input { display: none; } -.image-upload__p { +input { + background-color: #f3f4f6; +} + +.image_upload__p { color: #9ca3af; } diff --git a/src/components/ui/Logo.jsx b/src/components/ui/Logo.jsx new file mode 100644 index 000000000..6c257cd8e --- /dev/null +++ b/src/components/ui/Logo.jsx @@ -0,0 +1,15 @@ +import { Link } from "react-router-dom"; +import styles from "./Logo.module.css"; +import logoImg from "../../assets/logo.png"; + +function Logo() { + return ( + <> + + 판다마켓 로고 + + + ); +} + +export default Logo; diff --git a/src/components/ui/Logo.module.css b/src/components/ui/Logo.module.css new file mode 100644 index 000000000..4321dd15c --- /dev/null +++ b/src/components/ui/Logo.module.css @@ -0,0 +1,11 @@ +/* Tablet size*/ +@media screen and (max-width: 1199px) { + .logo { + } +} +/*Mobile size*/ +@media screen and (max-width: 767px) { + .logo { + height: 40px; + } +} diff --git a/src/components/ui/Nav.jsx b/src/components/ui/Nav.jsx new file mode 100644 index 000000000..75711e4bf --- /dev/null +++ b/src/components/ui/Nav.jsx @@ -0,0 +1,55 @@ +import { NavLink, useLocation } from "react-router-dom"; +import Container from "../container/Container"; +import styles from "./Nav.module.css"; +import Logo from "./Logo"; +import Button from "./Button"; +import userIcon from "../../assets/userIcon.png"; + +function Nav() { + const location = useLocation(); + + return ( +
            + +
              + +
            • + { + return { + color: + isActive && location.pathname === "/free" ? "#3692ff" : "", + }; + }} + > + 자유게시판 + +
            • +
            • + { + return { + color: + isActive || location.pathname === "/additem" + ? "#3692ff" + : "", + }; + }} + > + 중고마켓 + +
            • +
            + {location.pathname === "/additem" ? ( + 유저 아이콘 + ) : ( + + )} +
            +
            + ); +} + +export default Nav; diff --git a/src/components/Nav.module.css b/src/components/ui/Nav.module.css similarity index 60% rename from src/components/Nav.module.css rename to src/components/ui/Nav.module.css index 92276566e..9447db4ed 100644 --- a/src/components/Nav.module.css +++ b/src/components/ui/Nav.module.css @@ -1,8 +1,7 @@ .nav { position: relative; z-index: 1; - padding: 15px 0; - background-color: #fff; + background-color: #ffffff; border-bottom: 1px solid #dfdfdf; width: 100%; } @@ -13,22 +12,8 @@ justify-content: space-between; } -.login__button { - padding: 12px 23px 12px 23px; - border-radius: 8px; - background-color: #3692ff; - color: #ffffff; - border: none; - font-size: 16px; - font-weight: 600; - line-height: 19.09px; -} - -.visit__link:focus { - color: #3692ff; -} - -.item { +.item a { + color: #4b5563; font-weight: 700; line-height: 21.48px; font-size: 18px; @@ -46,3 +31,10 @@ ul.menu { ul.menu > li:not(:last-child) { margin-right: 30px; } + +@media (max-width: 767px) { + .item { + font-size: 16px; + line-height: 19.09px; + } +} diff --git a/src/components/ui/PaginationBar.jsx b/src/components/ui/PaginationBar.jsx new file mode 100644 index 000000000..6c49a2cd9 --- /dev/null +++ b/src/components/ui/PaginationBar.jsx @@ -0,0 +1,23 @@ +import styles from "./PaginationBar.module.css"; +import arrowLeft from "../../assets/arrow-left.png"; +import arrowRight from "../../assets/arrow-right.png"; + +function PaginationBar() { + const pages = [1, 2, 3, 4, 5]; + + return ( +
            + + {pages.map((page) => ( + + ))} + +
            + ); +} + +export default PaginationBar; diff --git a/src/components/ui/PaginationBar.module.css b/src/components/ui/PaginationBar.module.css new file mode 100644 index 000000000..0cb73ae47 --- /dev/null +++ b/src/components/ui/PaginationBar.module.css @@ -0,0 +1,24 @@ +.paginationBar { + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + margin-top: 40px; + margin-bottom: 113px; +} + +.paginationButton, +.currentPageButton { + width: 40px; + height: 40px; + border: 1px solid #e5e7eb; + color: #6b7280; + padding: 12.5px; + border-radius: 40px; + font-weight: 600; + line-height: 19.09px; + display: flex; + align-items: center; + justify-content: center; + gap: 12.5px; +} diff --git a/src/components/ProductsListItem.js b/src/components/ui/Product.jsx similarity index 58% rename from src/components/ProductsListItem.js rename to src/components/ui/Product.jsx index 2c5fe40f2..3fd1f1094 100644 --- a/src/components/ProductsListItem.js +++ b/src/components/ui/Product.jsx @@ -1,16 +1,16 @@ -import "./ProductsListItem.css"; -import favoriteImg from "../assets/favorite.png"; +import styles from "./Product.module.css"; +import favoriteImg from "../../assets/favorite.png"; -function ProductsListItem({ product }) { +function Product({ product }) { return (
            {product.name}
            -

            {product.description}

            +

            {product.description}

            {product.price}원

            favorite-img @@ -21,4 +21,4 @@ function ProductsListItem({ product }) { ); } -export default ProductsListItem; +export default Product; diff --git a/src/components/ui/Product.module.css b/src/components/ui/Product.module.css new file mode 100644 index 000000000..5b5a01290 --- /dev/null +++ b/src/components/ui/Product.module.css @@ -0,0 +1,15 @@ +.productImg { + max-width: 100%; + width: 282px; + height: 282px; + border-radius: 16px; + margin-bottom: 16px; +} + +.description { + display: inline-block; + width: 180px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/src/components/ui/ProductListContainer.jsx b/src/components/ui/ProductListContainer.jsx new file mode 100644 index 000000000..587848096 --- /dev/null +++ b/src/components/ui/ProductListContainer.jsx @@ -0,0 +1,50 @@ +import ProductsList from "../ui/ProductsList"; +import BestProductsList from "../ui/BestProductsList"; +import styles from "./ProductListContainer.module.css"; +import Button from "../ui/Button"; +import { Link } from "react-router-dom"; +import ToggleMenuContainer from "../container/ToggleMenuContainer"; +import PaginationBar from "./PaginationBar"; + +function ProductListContainer({ + favoriteProducts, + sortedProducts, + searchText, + handleToggleClick, + setSearchText, +}) { + return ( +
            +
            +

            베스트 상품

            + +
            +
            +

            전체 상품

            +
            +
            +
            + setSearchText(e.target.value)} + autoComplete="off" + > +
            + + + +
            + +
            +
            +
            + +
            + +
            + ); +} + +export default ProductListContainer; diff --git a/src/components/ui/ProductListContainer.module.css b/src/components/ui/ProductListContainer.module.css new file mode 100644 index 000000000..6029e3e29 --- /dev/null +++ b/src/components/ui/ProductListContainer.module.css @@ -0,0 +1,48 @@ +h2 { + font-size: 20px; + line-height: 28px; + margin-top: 6px; + margin-bottom: 6px; +} + +.search_bar { + display: flex; + align-items: center; +} + +.search_container { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 40px; + margin-bottom: 24px; +} + +.search_bar__form { + display: flex; +} + +.search_bar__form input { + background-image: url("../../assets/search.png"); + background-repeat: no-repeat; + border-radius: 12px; + padding: 9px 20px 9px 44px; + background-position: 16px 50%; + gap: 10px; + border: none; + background-color: #f3f4f6; +} + +.search_bar__form input:focus { + outline: none; +} + +h2.best_product { + margin-top: 24px; +} + +.search_bar_wrapper { + position: relative; + display: flex; + align-items: center; +} diff --git a/src/components/ProductsList.js b/src/components/ui/ProductsList.jsx similarity index 53% rename from src/components/ProductsList.js rename to src/components/ui/ProductsList.jsx index ea2740ad5..cd55c3227 100644 --- a/src/components/ProductsList.js +++ b/src/components/ui/ProductsList.jsx @@ -1,13 +1,14 @@ -import "./ProductsList.css"; -import ProductsListItem from "./ProductsListItem"; +import styles from "./ProductsList.module.css"; +import "./global.css"; +import Product from "./Product"; function ProductsList({ products }) { return ( -
              +
                {products.map((product) => { return (
              • - +
              • ); })} diff --git a/src/components/ProductsList.css b/src/components/ui/ProductsList.module.css similarity index 61% rename from src/components/ProductsList.css rename to src/components/ui/ProductsList.module.css index 4d0e661af..3d0437bd2 100644 --- a/src/components/ProductsList.css +++ b/src/components/ui/ProductsList.module.css @@ -1,4 +1,4 @@ -.Products-list { +.ProductsList { list-style: none; display: grid; grid-template-columns: repeat(5, 1fr); @@ -7,19 +7,6 @@ padding: 0; } -.favorite-contents { - display: flex; - align-items: center; -} - -.favorite-contents img { - height: 16px; -} - -.favorite-contents h3 { - margin-left: 4px; -} - h2 { font-weight: 700; font-size: 16px; diff --git a/src/components/ProductRagistrationForm.js b/src/components/ui/RagistrationForm.jsx similarity index 66% rename from src/components/ProductRagistrationForm.js rename to src/components/ui/RagistrationForm.jsx index 476073026..597efb63a 100644 --- a/src/components/ProductRagistrationForm.js +++ b/src/components/ui/RagistrationForm.jsx @@ -1,38 +1,17 @@ -import { useState } from "react"; import FileInput from "./FileInput"; -import "./ProductRagistrationForm.css"; - -function ProductRagistrationForm() { - const [values, setValues] = useState({ - imageFile: null, - productName: "", - productDescription: "", - productPrice: "", - productTag: "", - }); - - const handleChange = (name, value) => { - setValues((prevValues) => ({ - ...prevValues, - [name]: value, - })); - }; - - const handleInputChange = (e) => { - const { name, value } = e.target; - handleChange(name, value); - }; - - const handleSubmit = (e) => { - e.preventDefault(); - console.log(values); - }; +import styles from "./RagistrationForm.module.css"; +function ProductRagistrationForm({ + values, + handleChange, + handleInputChange, + handleSubmit, +}) { return ( -
                -
                + +

                상품등록하기

                -
                @@ -51,6 +30,7 @@ function ProductRagistrationForm() { type="text" placeholder="상품명을 입력해주세요" required + autoComplete="off" > + {values.productTag && ( +

                + {values.productTag} + +

                + )} ); } diff --git a/src/components/ui/RagistrationForm.module.css b/src/components/ui/RagistrationForm.module.css new file mode 100644 index 000000000..6f08acac1 --- /dev/null +++ b/src/components/ui/RagistrationForm.module.css @@ -0,0 +1,96 @@ +form.product_ragistration_form { + display: flex; + flex-direction: column; + margin: 24px auto 0px; +} + +form.product_ragistration_form h1 { + font-size: 28px; + font-weight: 700; + line-height: 33.41px; +} + +textarea { + width: 1200px; + height: 200px; + border: none; + padding: 16px 0 16px 24px; +} + +input { + background-color: #f3f4f6; +} + +form.product_ragistration_form label { + font-size: 18px; + font-weight: 700; + line-height: 21.48px; + margin-bottom: 12px; + cursor: pointer; +} + +form.product_ragistration_form input { + gap: 10px; + border: none; + height: 56px; +} + +.product_ragistration_form input:focus, +textarea:focus { + outline: none; +} + +form.product_ragistration_form input, +textarea { + font-size: 16px; + font-weight: 400; + line-height: 24px; + border-radius: 12px; + background-color: #f3f4f6; + margin-bottom: 24px; +} + +.product_ragistration_form_wrapper { + display: flex; + justify-content: space-between; + align-items: center; +} + +form.product_ragistration_form:valid .disabledButton { + background-color: #3692ff; + cursor: pointer; +} + +.disabledButton { + background-color: #9ca3af; + color: #ffffff; + cursor: default; + padding: 12px 20px 12px 20px; + height: 42px; + width: 88px; + border-radius: 8px; + font-weight: 600; + line-height: 19.09px; +} + +.product_ragistration_form input { + padding: 16px 0 16px 24px; +} + +.tag { + width: 100px; + height: 48px; + background-color: #f9fafb; + border-radius: 26px; + padding: 12px 12px 12px 16px; + display: flex; + align-items: center; + justify-content: center; +} + +.delete_button { + background-color: #9ca3af; + border-radius: 50%; + color: #ffffff; + margin-left: 9px; +} diff --git a/src/components/ui/ToggleMenu.jsx b/src/components/ui/ToggleMenu.jsx new file mode 100644 index 000000000..a918cbbe4 --- /dev/null +++ b/src/components/ui/ToggleMenu.jsx @@ -0,0 +1,38 @@ +import styles from "./ToggleMenu.module.css"; +import arrowDownImg from "../../assets/arrow-down.png"; + +const OPTIONS = { + NEWEST: { label: "최신순", value: "newest" }, + MOST_LIKED: { label: "좋아요순", value: "likes" }, +}; + +function ToggleMenu({ onClick, onToggleMenu, isOpen }) { + return ( +
                + + {isOpen && ( +
                  +
                  +
                • onClick(OPTIONS.NEWEST.value)} + > + +
                • +
                  +
                • onClick(OPTIONS.MOST_LIKED.value)} + > + +
                • +
                + )} +
                + ); +} + +export default ToggleMenu; diff --git a/src/components/ToggleMenu.module.css b/src/components/ui/ToggleMenu.module.css similarity index 79% rename from src/components/ToggleMenu.module.css rename to src/components/ui/ToggleMenu.module.css index b05ea05df..405c106a1 100644 --- a/src/components/ToggleMenu.module.css +++ b/src/components/ui/ToggleMenu.module.css @@ -5,7 +5,7 @@ .iconButton { border: 1px solid #ebebeb; cursor: pointer; - padding: 12px 30px; + padding: 12px 20px 12px 20px; border-radius: 12px; gap: 10px; margin: 0; @@ -15,9 +15,8 @@ align-items: center; } -.toggleMenu .iconText { - position: relative; - left: -15px; +.iconText { + font-size: 16px; } ul.popup { @@ -44,8 +43,7 @@ ul.popup li.disabled { background-color: #ffffff; color: #c4c4c4; user-select: none; - cursor: default; - border: 1px 1px 0px 1px; + cursor: pointer; height: 42px; } @@ -61,9 +59,3 @@ button { .newestBtn { border-bottom: 1px solid #e5e7eb; } - -.iconButton img { - position: absolute; - right: 12px; - height: 7.42px; -} diff --git a/src/components/ui/global.css b/src/components/ui/global.css new file mode 100644 index 000000000..c262ed07e --- /dev/null +++ b/src/components/ui/global.css @@ -0,0 +1,16 @@ +.favorite-contents { + display: flex; + align-items: center; +} + +.favorite-contents img { + height: 16px; +} + +.favorite-contents h3 { + margin-left: 4px; +} + +.favorite-contents h3 { + color: #4b5563; +} diff --git a/src/pages/FreeBoardPage.js b/src/pages/FreeBoardPage.js new file mode 100644 index 000000000..b51c453e5 --- /dev/null +++ b/src/pages/FreeBoardPage.js @@ -0,0 +1,9 @@ +function FreeBoardPage() { + return ( + <> +

                자유게시판

                + + ); +} + +export default FreeBoardPage; diff --git a/src/pages/ProductRagistrationPage.js b/src/pages/ProductRagistrationPage.js index ea00b9c8d..c2cf4acb2 100644 --- a/src/pages/ProductRagistrationPage.js +++ b/src/pages/ProductRagistrationPage.js @@ -1,7 +1,7 @@ -import ProductRagistrationForm from "../components/ProductRagistrationForm"; +import RagistrationContainer from "../components/container/RagistrationContainer"; function ProductRagistrationPage() { - return ; + return ; } export default ProductRagistrationPage; diff --git a/src/pages/UsedMarketPage.css b/src/pages/UsedMarketPage.css deleted file mode 100644 index e0b19aea4..000000000 --- a/src/pages/UsedMarketPage.css +++ /dev/null @@ -1,66 +0,0 @@ -h2 { - font-size: 20px; - line-height: 28px; - letter-spacing: 2%; - margin-top: 6px; - margin-bottom: 6px; -} - -.search-bar { - display: flex; - align-items: center; -} - -.search-container { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: 40px; - margin-bottom: 24px; -} - -.search-bar__form { - display: flex; -} - -.search-bar__form input { - border-radius: 12px; - padding: 9px 20px 9px 20px; - gap: 10px; - border: none; - background-color: #f3f4f6; -} - -.search-bar__form input:focus { - outline: none; -} - -.add-button { - border-radius: 8px; - padding: 12px 23px 12px 23px; - gap: 10px; - border: none; - background-color: #3692ff; - color: #ffffff; - font-weight: 600; - font-size: 16px; - line-height: 19.09px; - margin-left: 12px; - margin-right: 12px; -} - -h2.best-product { - margin-top: 24px; - margin-bottom: 12px; -} - -.search-bar-wrapper { - position: relative; - display: flex; - align-items: center; -} - -.search-bar-wrapper__image { - position: absolute; - left: 5px; -} diff --git a/src/pages/UsedMarketPage.js b/src/pages/UsedMarketPage.js index e24328602..69309d656 100644 --- a/src/pages/UsedMarketPage.js +++ b/src/pages/UsedMarketPage.js @@ -1,83 +1,7 @@ -import { Link } from "react-router-dom"; -import { useState, useEffect } from "react"; -import { getProducts } from "../api"; -import ProductsList from "../components/ProductsList"; -import BestProductsList from "../components/BestProductsList"; -import "./UsedMarketPage.css"; -import searchImg from "../assets/search.png"; -import ToggleMenu from "../components/ToggleMenu"; +import ListContainer from "../components/container/ListContainer"; function UsedMarketPage() { - const [products, setProducts] = useState([]); - const [order, setOrder] = useState("createdAt"); - const [favoriteProducts, setFavoriteProducts] = useState([]); - - const [searchText, setSearchText] = useState(""); - - const sortedProducts = products.sort((a, b) => b[order] - a[order]); - - const handleLoad = async () => { - const { list } = await getProducts(); - setProducts(list); - }; - - useEffect(() => { - handleLoad(); - }, [order]); - - useEffect(() => { - const sortedFavoriteProducts = [...products].sort( - (a, b) => b.favoriteCount - a.favoriteCount - ); - setFavoriteProducts(sortedFavoriteProducts); - }, [products]); - - const handleToggleClick = (option) => { - if (option === "newest") { - setOrder("createdAt"); - } else if (option === "likes") { - setOrder("favoriteCount"); - } - }; - - return ( -
                -
                -

                베스트 상품

                - -
                -
                -

                전체 상품

                -
                -
                -
                - setSearchText(e.target.value)} - > - {searchText ? null : ( - 검색 - )} -
                - - 상품 등록하기 - -
                - -
                -
                -
                - -
                -
                - ); + return ; } export default UsedMarketPage; From f3c1d466b1eb081c14ef97ddb05769479bc96a8a Mon Sep 17 00:00:00 2001 From: baehyunji Date: Fri, 31 May 2024 22:39:38 +0900 Subject: [PATCH 07/10] =?UTF-8?q?feat:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/back-icon.png | Bin 0 -> 344 bytes src/assets/heart.png | Bin 0 -> 878 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/assets/back-icon.png create mode 100644 src/assets/heart.png diff --git a/src/assets/back-icon.png b/src/assets/back-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c80f4478e7b2ef91c4a017a1e50e89e25d00154c GIT binary patch literal 344 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoM!3HGxw}u@9Qk(@Ik;M!Q+`=Ht$S`Y;1W=H% zILO_JVcj{Imp~3nx}&cn1H;CC?mvmFK>k}#7srqa#P z;N6Ko-@ED@^d^>W>UjM(;JU$$ZwmUa*oxD=69wbylC*Jd6=rOy+C n9`;#zzki~wmc*+2hnN4knAXGvk9PkAdYr-2)z4*}Q$iB}1w?|6 literal 0 HcmV?d00001 diff --git a/src/assets/heart.png b/src/assets/heart.png new file mode 100644 index 0000000000000000000000000000000000000000..89b6bbe3683584dd46a2cfd719314cf09ce66eed GIT binary patch literal 878 zcmV-!1CjiRP)0{GVJ0lZVbyOV&oy1Z%G91Eap_*RJW%+rv z=13CtSPzbW!0`Q8-xvzCp;ABTR04eJ6F~=fQ($9I*qqYjd7kQqC#|X_tkob|TANCa zL|`QCg}R!aZFy=xwzc+B*``NJu=R8pi2(wu6@-g75baOHVC=Y$q;(?cFgjrv8KY?q zq3%!$)~ZUuUX4ji8=6bn%q11^9hZcJB2mzMs}|aA|2WMc*1PjX`?CqyBO8Z=YX_sjm8yl(w0=pauiAXGSks94ONAWOn-b!=M$eag~O$3CV3~M(1 z;MqW&o!r9S&1PLW!*p|5hQcPYV54qO83vah6d(Qi-fp$C_8?lel=KOE?jf7phNoU- z>1Ps%Nucxn1OZzJQJycB2;Hoerptp#PKfsqXHa%3jk^2#GW(YjV8CG~YQKwkQc0{- z-5M?wJM5yRKW=QeAFg6s&x&CnLv!Sit;cEt+O1ZSvf7uZWKIphU54I6I=s-0p~(qH&)C8H~;_u07*qoM6N<$ Ef||C1l>h($ literal 0 HcmV?d00001 From 6c9a3d23090d16e9f5a907a4084855b87963e2b5 Mon Sep 17 00:00:00 2001 From: baehyunji Date: Fri, 31 May 2024 22:49:09 +0900 Subject: [PATCH 08/10] =?UTF-8?q?feat:=EC=83=81=EC=84=B8=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Main.js | 2 + .../container/ProductDeatilsContainer.jsx | 57 ++++++++ src/components/ui/CommentDetails.jsx | 50 +++++++ src/components/ui/CommentDetails.module.css | 109 +++++++++++++++ src/components/ui/ProductDetails.jsx | 28 ++++ src/components/ui/ProductDetails.module.css | 127 ++++++++++++++++++ src/pages/ProductDetailedPage.js | 7 + src/utils.js | 18 +++ 8 files changed, 398 insertions(+) create mode 100644 src/components/container/ProductDeatilsContainer.jsx create mode 100644 src/components/ui/CommentDetails.jsx create mode 100644 src/components/ui/CommentDetails.module.css create mode 100644 src/components/ui/ProductDetails.jsx create mode 100644 src/components/ui/ProductDetails.module.css create mode 100644 src/pages/ProductDetailedPage.js create mode 100644 src/utils.js diff --git a/src/Main.js b/src/Main.js index b0bda43a5..5a5c100f1 100644 --- a/src/Main.js +++ b/src/Main.js @@ -3,6 +3,7 @@ import App from "./components/ui/App"; import UsedMarketPage from "./pages/UsedMarketPage"; import ProductRagistrationPage from "./pages/ProductRagistrationPage"; import FreeBoardPage from "./pages/FreeBoardPage"; +import ProductDetailedPage from "./pages/ProductDetailedPage"; function Main() { return ( @@ -12,6 +13,7 @@ function Main() { } /> } /> } /> + } /> diff --git a/src/components/container/ProductDeatilsContainer.jsx b/src/components/container/ProductDeatilsContainer.jsx new file mode 100644 index 000000000..6c7ca9f56 --- /dev/null +++ b/src/components/container/ProductDeatilsContainer.jsx @@ -0,0 +1,57 @@ +import ProductDetails from "../ui/ProductDetails"; +import { useParams } from "react-router-dom"; +import { useEffect, useState } from "react"; +import { getProduct, getComment } from "../../api"; +import CommentDetails from "../ui/CommentDetails"; + +function ProductDetailsContainer() { + const { productId } = useParams(); + const [product, setProduct] = useState(null); + const [comments, setComments] = useState([]); + + useEffect(() => { + const detailedProduct = async () => { + try { + const productData = await getProduct(productId); + setProduct(productData); + } catch (e) { + if (e.response) { + console.log(e.response.status); + console.log(e.response.data); + } else { + console.log("리퀘스트가 실패했습니다."); + } + } + }; + + const detailedComment = async () => { + const params = { limit: 3 }; + try { + const commentData = await getComment({ productId, params }); + setComments(commentData.list); + } catch (e) { + if (e.response) { + console.log(e.response.status); + console.log(e.response.data); + } else { + console.log("리퀘스트가 실패했습니다."); + } + } + }; + detailedComment(); + detailedProduct(); + }, [productId]); + + if (!product) { + return
                로딩중
                ; + } + + return ( + <> + + + + ); +} + +export default ProductDetailsContainer; diff --git a/src/components/ui/CommentDetails.jsx b/src/components/ui/CommentDetails.jsx new file mode 100644 index 000000000..dd94af0fa --- /dev/null +++ b/src/components/ui/CommentDetails.jsx @@ -0,0 +1,50 @@ +import styles from "../ui/CommentDetails.module.css"; +import backIcon from "../../assets/back-icon.png"; +import { Link } from "react-router-dom"; +import { elapsedTime } from "../../utils"; + +function CommentDetails({ comments }) { + return ( +
                +
                +

                문의하기

                +
                + +
                + +
                +
                +
                +
                + {comments.map((comment) => ( +
                +

                {comment.content}

                +
                + 작성자 이미지 +
                +

                {comment.writer.nickname}

                +

                + {elapsedTime(comment.updatedAt)} +

                +
                +
                +
                + ))} +
                + + 목록으로 돌아가기 + 돌아가기 아이콘 + +
                + ); +} + +export default CommentDetails; diff --git a/src/components/ui/CommentDetails.module.css b/src/components/ui/CommentDetails.module.css new file mode 100644 index 000000000..b344375f2 --- /dev/null +++ b/src/components/ui/CommentDetails.module.css @@ -0,0 +1,109 @@ +.commentWrapper { + border-top: 1px solid #e5e7eb; + display: flex; + flex-direction: column; +} +.commentWrapper h2 { + font-weight: 600; + line-height: 19.09px; + margin-top: 24px; + margin-bottom: 16px; +} + +.commentWrapper input { + border: none; + outline: none; + border-radius: 12px; + padding: 16px 24px 16px 24px; + width: 100%; + min-height: 104px; + background-color: #f3f4f6; +} + +.buttonWrapper { + text-align: right; + margin-top: 16px; + margin-bottom: 24px; +} + +.disabledButton { + background-color: #9ca3af; + color: #ffffff; + cursor: default; + padding: 12px 20px 12px 20px; + height: 42px; + width: 88px; + border-radius: 8px; + font-weight: 600; + line-height: 19.09px; +} + +form:valid .disabledButton { + background-color: #3692ff; + cursor: pointer; +} + +.content { + line-height: 22.4px; + margin-top: 24px; + margin-bottom: 24px; +} + +.contentWrapper { + display: flex; + border-bottom: 1px solid #e5e7eb; + margin-bottom: 40px; +} + +.comment img { + width: 40px; + height: 40px; +} + +.commentContent { + margin-left: 8px; + margin-bottom: 5px; +} + +.nickname { + margin: 0; + margin-bottom: 4px; + font-size: 14px; + line-height: 16.71px; + color: #4b5563; +} + +.updatedAt { + font-size: 12px; + line-height: 14.32px; + margin: 0; + margin-bottom: 29px; + color: #9ca3af; +} + +.goBackButton { + width: 240px; + height: 48px; + border-radius: 40px; + padding: 0; + background-color: #3692ff; + border: none; + cursor: pointer; + padding: 12px 71px 12px 71px; + margin: 0 auto 250px; + display: flex; + align-items: center; + justify-content: center; + text-wrap: nowrap; +} + +.goBackButton span { + color: #ffffff; + font-weight: 600; + font-size: 18px; + line-height: 24px; +} + +.backIcon { + margin-left: 10px; +} diff --git a/src/components/ui/ProductDetails.jsx b/src/components/ui/ProductDetails.jsx new file mode 100644 index 000000000..1c23f6bd5 --- /dev/null +++ b/src/components/ui/ProductDetails.jsx @@ -0,0 +1,28 @@ +import styles from "../ui/ProductDetails.module.css"; +import heartIcon from "../../assets/heart.png"; + +function ProductDetails({ product }) { + return ( +
                + {product.name} +
                +

                {product.name}

                +

                {product.price}원

                +

                상품 소개

                +

                {product.description}

                +

                상품 태그

                +

                #{product.tags}

                + +
                +
                + ); +} + +export default ProductDetails; diff --git a/src/components/ui/ProductDetails.module.css b/src/components/ui/ProductDetails.module.css new file mode 100644 index 000000000..f3030e477 --- /dev/null +++ b/src/components/ui/ProductDetails.module.css @@ -0,0 +1,127 @@ +.productImg { + width: 486px; + height: 486px; + margin-right: 24px; + border-radius: 20px; +} + +.productWrapper { + max-width: 996px; + display: flex; + justify-content: space-between; + padding-top: 24px; + padding-bottom: 32px; + margin: 0 auto; +} + +.productContent { + width: 486px; + height: 486px; +} + +.productContent h2 { + font-weight: 600; + font-size: 24px; + line-height: 28.64px; + color: #1f2937; + margin: 0; +} + +.productContent h1 { + font-weight: 600; + font-size: 40px; + line-height: 47.73px; + color: #1f2937; + margin: 0; + margin-top: 16px; + padding-bottom: 16px; +} + +.productContent p { + font-weight: 500; + font-size: 14px; + line-height: 16.71px; + color: #4b5563; + margin-top: 16px; + margin-bottom: 8px; +} + +.productContent h3 { + line-height: 22.4px; + color: #1f2937; + margin: 0; +} + +.productContent h3 { + margin-bottom: 24px; +} + +.favoriteTag { + width: 87px; + height: 40px; + border: 1px solid #e5e7eb; + border-radius: 35px; + padding: 4px 12px 4px 12px; + margin-top: 124px; + gap: 10px; + display: flex; + align-items: center; + color: #6b7280; + font-weight: 500; + line-height: 19.09px; +} + +/* Tablet size*/ +@media screen and (max-width: 1199px) { + .productContent h2 { + font-size: 20px; + line-height: 23.87px; + } + + .productContent h1 { + font-size: 32px; + line-height: 38.19px; + } + + .productContent p { + line-height: 19.6px; + } + + .productImg { + width: 340px; + height: 340px; + } + + .favoriteTag { + margin-top: 25px; + } +} +/*Mobile size*/ +@media screen and (max-width: 767px) { + .productContent h2 { + font-size: 16px; + line-height: 19.09px; + } + + .productContent h1 { + font-size: 24px; + line-height: 28.64px; + } + + .productContent p { + line-height: 16.71px; + } + + .favoriteTag img { + width: 24px; + } + + .productImg { + width: 343px; + height: 343px; + } + + .favoriteTag { + margin-top: 25px; + } +} diff --git a/src/pages/ProductDetailedPage.js b/src/pages/ProductDetailedPage.js new file mode 100644 index 000000000..9da6d98e4 --- /dev/null +++ b/src/pages/ProductDetailedPage.js @@ -0,0 +1,7 @@ +import ProductDetailsContainer from "../components/container/ProductDeatilsContainer"; + +function ProductDetailedPage() { + return ; +} + +export default ProductDetailedPage; diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 000000000..025d0ce8b --- /dev/null +++ b/src/utils.js @@ -0,0 +1,18 @@ +export const elapsedTime = (date) => { + const start = new Date(date); + const end = new Date(); + + const seconds = Math.floor((end.getTime() - start.getTime()) / 1000); + if (seconds < 60) return "방금 전"; + + const minutes = seconds / 60; + if (minutes < 60) return `${Math.floor(minutes)}분 전`; + + const hours = minutes / 60; + if (hours < 24) return `${Math.floor(hours)}시간 전`; + + const days = hours / 24; + if (days < 7) return `${Math.floor(days)}일 전`; + + return start.toLocaleDateString(); +}; From 178c6b873ba1df17426caa5dfb9501545fd2f6e8 Mon Sep 17 00:00:00 2001 From: baehyunji Date: Fri, 31 May 2024 22:50:42 +0900 Subject: [PATCH 09/10] =?UTF-8?q?feat:=20api.js=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/api.js b/src/api.js index 436f1d354..647090d8e 100644 --- a/src/api.js +++ b/src/api.js @@ -5,7 +5,19 @@ const instance = axios.create({ timeout: 3000, }); -export async function getProducts() { - const res = await instance.get("/products/"); +export async function getProducts(params = {}) { + const query = new URLSearchParams(params).toString(); + const res = await instance.get(`/products?${query}`); + return res.data; +} + +export async function getProduct(productId) { + const res = await instance.get(`/products/${productId}`); + return res.data; +} + +export async function getComment({ productId, params }) { + const query = new URLSearchParams(params).toString(); + const res = await instance.get(`/products/${productId}/comments?${query}`); return res.data; } From 711b5ffc0fdb4ba0778a9cab987745a078c6ed25 Mon Sep 17 00:00:00 2001 From: baehyunji Date: Fri, 31 May 2024 22:51:52 +0900 Subject: [PATCH 10/10] =?UTF-8?q?fix:=20=EA=B0=84=EB=8B=A8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/container/ListContainer.jsx | 5 +-- .../container/RagistrationContainer.jsx | 1 - src/components/ui/BestProduct.jsx | 19 +++++++----- src/components/ui/BestProduct.module.css | 31 +++++++++++++++---- src/components/ui/Button.module.css | 6 ---- src/components/ui/Product.jsx | 19 +++++++----- src/components/ui/Product.module.css | 31 +++++++++++++++---- .../ui/ProductListContainer.module.css | 19 +++++++++--- src/components/ui/ProductsList.module.css | 12 ------- 9 files changed, 89 insertions(+), 54 deletions(-) diff --git a/src/components/container/ListContainer.jsx b/src/components/container/ListContainer.jsx index 43a68d96d..3599a1862 100644 --- a/src/components/container/ListContainer.jsx +++ b/src/components/container/ListContainer.jsx @@ -10,7 +10,7 @@ function ListContainer() { const sortedProducts = products.sort((a, b) => b[order] - a[order]); - const handleLoad = async () => { + const loadList = async () => { try { const { list } = await getProducts(); setProducts(list); @@ -25,7 +25,7 @@ function ListContainer() { }; useEffect(() => { - handleLoad(); + loadList(); }, [order]); useEffect(() => { @@ -42,6 +42,7 @@ function ListContainer() { setOrder("favoriteCount"); } }; + return ( { e.preventDefault(); - console.log(values); setValues({ imageFile: null, diff --git a/src/components/ui/BestProduct.jsx b/src/components/ui/BestProduct.jsx index cf3cbd1e2..970d2c1f9 100644 --- a/src/components/ui/BestProduct.jsx +++ b/src/components/ui/BestProduct.jsx @@ -1,20 +1,23 @@ import styles from "./BestProduct.module.css"; import favoriteImg from "../../assets/favorite.png"; +import { Link } from "react-router-dom"; function BestProduct({ product }) { return (
                - {product.name} + + {product.name} +
                -

                {product.description}

                -

                {product.price}원

                +

                {product.name}

                +

                {product.price}원

                favorite-img -

                {product.favoriteCount}

                +

                {product.favoriteCount}

                diff --git a/src/components/ui/BestProduct.module.css b/src/components/ui/BestProduct.module.css index 60dd388a0..2d0276e7d 100644 --- a/src/components/ui/BestProduct.module.css +++ b/src/components/ui/BestProduct.module.css @@ -6,10 +6,29 @@ margin-bottom: 16px; } -.best_description { - display: inline-block; - width: 180px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; +.best_name { + font-weight: 500; + font-size: 14px; + line-height: 16.71px; + margin: 0; + margin-top: 16px; +} + +.price { + font-weight: 700; + line-height: 19.09px; + margin-top: 6px; + margin-bottom: 6px; +} + +.best_name, +.price { + color: #1f2937; +} + +.favorite { + font-weight: 500; + font-size: 12px; + line-height: 14.32px; + color: #4b5563; } diff --git a/src/components/ui/Button.module.css b/src/components/ui/Button.module.css index 94ec2b1be..51b31d2ce 100644 --- a/src/components/ui/Button.module.css +++ b/src/components/ui/Button.module.css @@ -23,9 +23,3 @@ margin-left: 12px; margin-right: 12px; } -/* Tablet size*/ -@media screen and (max-width: 1199px) { -} -/*Mobile size*/ -@media screen and (max-width: 767px) { -} diff --git a/src/components/ui/Product.jsx b/src/components/ui/Product.jsx index 3fd1f1094..5c60e35c0 100644 --- a/src/components/ui/Product.jsx +++ b/src/components/ui/Product.jsx @@ -1,20 +1,23 @@ import styles from "./Product.module.css"; import favoriteImg from "../../assets/favorite.png"; +import { Link } from "react-router-dom"; function Product({ product }) { return (
                - {product.name} + + {product.name} +
                -

                {product.description}

                -

                {product.price}원

                +

                {product.name}

                +

                {product.price}원

                favorite-img -

                {product.favoriteCount}

                +

                {product.favoriteCount}

                diff --git a/src/components/ui/Product.module.css b/src/components/ui/Product.module.css index 5b5a01290..0c948038a 100644 --- a/src/components/ui/Product.module.css +++ b/src/components/ui/Product.module.css @@ -6,10 +6,29 @@ margin-bottom: 16px; } -.description { - display: inline-block; - width: 180px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; +.name { + font-weight: 500; + font-size: 14px; + line-height: 16.71px; + margin: 0; + margin-top: 16px; +} + +.price { + font-weight: 700; + line-height: 19.09px; + margin-top: 6px; + margin-bottom: 6px; +} + +.name, +.price { + color: #1f2937; +} + +.favorite { + font-weight: 500; + font-size: 12px; + line-height: 14.32px; + color: #4b5563; } diff --git a/src/components/ui/ProductListContainer.module.css b/src/components/ui/ProductListContainer.module.css index 6029e3e29..5496c685c 100644 --- a/src/components/ui/ProductListContainer.module.css +++ b/src/components/ui/ProductListContainer.module.css @@ -1,8 +1,21 @@ -h2 { +.search_container h2 { font-size: 20px; line-height: 28px; margin-top: 6px; margin-bottom: 6px; + color: #1f2937; +} + +.search_container h2, +.best_product { + font-weight: 700; + font-size: 20px; + line-height: 28px; +} + +.best_product { + color: #111827; + margin-top: 24px; } .search_bar { @@ -37,10 +50,6 @@ h2 { outline: none; } -h2.best_product { - margin-top: 24px; -} - .search_bar_wrapper { position: relative; display: flex; diff --git a/src/components/ui/ProductsList.module.css b/src/components/ui/ProductsList.module.css index 3d0437bd2..e623dd52f 100644 --- a/src/components/ui/ProductsList.module.css +++ b/src/components/ui/ProductsList.module.css @@ -6,15 +6,3 @@ gap: 16px; padding: 0; } - -h2 { - font-weight: 700; - font-size: 16px; - line-height: 19.09px; -} - -h3 { - font-weight: 500; - font-size: 14px; - line-height: 16.71px; -}