diff --git a/package-lock.json b/package-lock.json index a1e590ee6..6245c1335 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,10 +11,15 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.7.9", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^7.0.2", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -630,9 +635,18 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, "engines": { "node": ">=6.9.0" }, @@ -1875,6 +1889,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -3953,6 +3979,12 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, "node_modules/@types/eslint": { "version": "8.44.2", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz", @@ -5285,6 +5317,31 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "license": "MIT", + "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.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "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", @@ -8294,15 +8351,16 @@ "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.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -14375,6 +14433,12 @@ "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==", + "license": "MIT" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -14671,6 +14735,55 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.0.2.tgz", + "integrity": "sha512-m5AcPfTRUcjwmhBzOJGEl6Y7+Crqyju0+TgTQxoS4SO+BkWbhOrcfZNq6wSWdl2BBbJbsAoBUb8ZacOFT+/JlA==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.0.2.tgz", + "integrity": "sha512-VJOQ+CDWFDGaWdrG12Nl+d7yHtLaurNgAQZVgaIy7/Xd+DojgmYLosFfZdGz1wpxmjJIAkAMVTKWcvkx1oggAw==", + "license": "MIT", + "dependencies": { + "react-router": "7.0.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-router/node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -15480,6 +15593,12 @@ "node": ">= 0.8.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -16512,6 +16631,12 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 7ff0d6b58..11437aad4 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,10 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.7.9", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^7.0.2", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" }, @@ -34,5 +36,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11" } } diff --git a/public/images/arrow_next.svg b/public/images/arrow_next.svg new file mode 100644 index 000000000..4261e52df --- /dev/null +++ b/public/images/arrow_next.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/arrow_prev.svg b/public/images/arrow_prev.svg new file mode 100644 index 000000000..6b3286f2f --- /dev/null +++ b/public/images/arrow_prev.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/detail_like_icon.svg b/public/images/detail_like_icon.svg new file mode 100644 index 000000000..7b994fbe7 --- /dev/null +++ b/public/images/detail_like_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/noinquiry_img.svg b/public/images/noinquiry_img.svg new file mode 100644 index 000000000..5444cbbbc --- /dev/null +++ b/public/images/noinquiry_img.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/public/images/return_icon.svg b/public/images/return_icon.svg new file mode 100644 index 000000000..014359ce4 --- /dev/null +++ b/public/images/return_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/setting_icon.svg b/public/images/setting_icon.svg new file mode 100644 index 000000000..85b6a28f4 --- /dev/null +++ b/public/images/setting_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/App.js b/src/App.js index 1c433f75f..d953960ff 100644 --- a/src/App.js +++ b/src/App.js @@ -1,15 +1,18 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom'; import Home from './components/page/Home'; -import ProductReg from './components/page/ProductReg'; -import ProductList from './components/page/ProductList'; +import ProductListPage from './components/page/ProductListPage'; +import ProductRegPage from './components/page/ProductRegPage'; +import ProductDetailPage from './components/page/ProductDetailPage'; + function App() { return ( } /> - } /> - } /> + } /> + } /> + } /> ); diff --git a/src/asset/css/style.css b/src/asset/css/style.css index 32d0f058e..d1e82539a 100644 --- a/src/asset/css/style.css +++ b/src/asset/css/style.css @@ -44,7 +44,6 @@ /* 공통-레이아웃 */ main { margin-top: 70px; - min-height: 600px; } p.title { @@ -208,7 +207,7 @@ p.title { #header nav .depth01 li h2 a { display: block; - padding: 0 15px; + padding: 24px 15px; font-size: 18px; font-weight: 700; color: var(--gray03); @@ -272,8 +271,53 @@ p.title { } */ +@media (max-width: 1199px) { + + #header nav .depth01 li h2 a { + padding: 25px 15px; + font-size: 16px; + } + +} + +@media (max-width: 767px) { + + #header .logo h1 a img { + width: 120px; + } + + #header .logo { + margin-right: 20px; + } + + #header nav .depth01 li h2 a { + padding: 27px 10px; + font-size: 14px; + } + +} + +@media (max-width: 414px) { + + #header .logo h1 a img { + width: 100px; + } + + #header nav .depth01 li h2 a { + padding: 28px 8px; + font-size: 12px; + } + + #header .logo { + margin-right: 10px; + } + +} + + /* 공통-푸터 */ #footer { + position: relative; padding: 33px 0 0; height: 160px; background-color: #111827; @@ -620,7 +664,8 @@ p.title { } #sub { - padding: 24px 0 58px; + padding: 24px 0 100px; + min-height: 1000px; background-color: #fcfcfc; } @@ -737,6 +782,7 @@ p.title { } #sub .productListBox .productBox { + position: relative; margin-bottom: 49px; } @@ -788,24 +834,23 @@ p.title { top: -9px; right: 0; height: 42px; + border-radius: 12px; } .searchWrap .searchBox { + position: relative; width: 325px; + display: flex; } -.searchWrap .searchBox .searchBtnBox { +.searchWrap .searchBox::after { + content: ''; position: absolute; - top: 14px; - left: 20px; -} - -.searchWrap .searchBox .searchBtnBox .searchBtn { - display: block; + top: 13px; + left: 17px; width: 15px; height: 15px; - background: url(/public/images/search_icon.svg)no-repeat center; - background-size: cover; + background: url(/public/images/search_icon.svg)no-repeat center center; } .searchWrap .searchBox input { @@ -864,10 +909,11 @@ p.title { .searchWrap .selectBox .select.active+.viewList { opacity: 1; - z-index: 11; + z-index: 99; } .searchWrap .selectBox .viewList { + position: relative; opacity: 0; margin-top: 8px; border: 1px solid #E5E7EB; @@ -875,6 +921,7 @@ p.title { overflow: hidden; z-index: -1; transition: all .3s; + background-color: var(--white01); } .searchWrap .selectBox .viewList li { @@ -910,20 +957,586 @@ p.title { } /* 상품등록 */ -.productRegWrap .productRegBox{position: relative;} -.productRegWrap .LayoutBox{display: flex; gap: 0 24px;} -.productRegWrap .productUploadeImg{position: relative; width: 282px; height: 282px; border-radius: 12px; overflow: hidden;} -.productRegWrap .productUploadeImg img{display: block; width: 100%;} -.productRegWrap .productUploadeImg .delBtn{position: absolute; top:12px; right:12px; width: 20px; height: 24px; background: url(/public/images/del_btn_icon.png)no-repeat center; background-size: cover;} +.productRegWrap .productRegBox { + position: relative; +} + +.productRegWrap .LayoutBox { + display: flex; + gap: 0 24px; +} + +.productRegWrap .productUploadeImg { + position: relative; + width: 282px; + height: 282px; + border-radius: 12px; + overflow: hidden; +} + +.productRegWrap .productUploadeImg img { + display: block; + width: 100%; +} + +.productRegWrap .productUploadeImg .delBtn { + position: absolute; + top: 12px; + right: 12px; + width: 20px; + height: 24px; + background: url(/public/images/del_btn_icon.png)no-repeat center; + background-size: cover; +} /* 폼 */ -.formBox{margin: 0 0 32px;} -.formBox .inputFileBox label{display: flex; align-items: center; justify-content: center; width: 282px; height: 282px; background-color: var(--gray04); border-radius: 12px; cursor: pointer;} -.formBox .inputFileBox label span{position: relative; display: flex; flex-direction: column; align-items: center; gap: 15px 0; color: var(--gray01); line-height: 1;} -.formBox .inputFileBox label span::before{content: ''; width: 48px; height: 48px; background: url(/public/images/add_btn_icon.png)no-repeat center; background-size: cover;} -.formBox .inputTextBox input{padding: 20px 20px 20px 24px; width: 100%; border-radius: 12px; background-color: var(--gray04); font-size: 16px;} -.formBox .inputTextBox input::placeholder{color: var(--gray01); font-size: 16px;} -.formBox .textAreaBox textarea{padding: 20px 20px 20px 24px; width: 100%; height: 282px; border: 0; background-color: var(--gray04); font-family: 'pretendard', sans-serif; font-size: 16px; border-radius: 12px;} -.formBox .textAreaBox textarea::placeholder{color: var(--gray01);} -.productRegBox .addBtn{position: absolute; top:0; right:0; display: block; width: 74px; height: 42px; line-height: 42px; text-align: center; background-color: var(--blue); border-radius: 8px; color: #fff; color: var(--gray04); font-size: 16px;} -.productRegBox .addBtn:disabled{background-color: var(--gray01); cursor: auto;} \ No newline at end of file +.formBox { + margin: 0 0 32px; +} + +.formBox .inputFileBox label { + display: flex; + align-items: center; + justify-content: center; + width: 282px; + height: 282px; + background-color: var(--gray04); + border-radius: 12px; + cursor: pointer; +} + +.formBox .inputFileBox label span { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + gap: 15px 0; + color: var(--gray01); + line-height: 1; +} + +.formBox .inputFileBox label span::before { + content: ''; + width: 48px; + height: 48px; + background: url(/public/images/add_btn_icon.png)no-repeat center; + background-size: cover; +} + +.formBox .inputTextBox input { + padding: 20px 20px 20px 24px; + width: 100%; + border-radius: 12px; + background-color: var(--gray04); + font-size: 16px; +} + +.formBox .inputTextBox input::placeholder { + color: var(--gray01); + font-size: 16px; +} + +.formBox .textAreaBox textarea { + padding: 20px 20px 20px 24px; + width: 100%; + height: 282px; + border: 0; + background-color: var(--gray04); + font-family: 'pretendard', sans-serif; + font-size: 16px; + border-radius: 12px; +} + +.formBox .textAreaBox textarea::placeholder { + color: var(--gray01); +} + +.productRegBox .addBtn { + position: absolute; + top: 0; + right: 0; +} + +.addBtn { + display: block; + width: 74px; + height: 42px; + line-height: 42px; + text-align: center; + background-color: var(--blue); + border-radius: 8px; + color: var(--white01); + font-size: 16px; +} + +.addBtn:disabled { + background-color: var(--gray01); + cursor: auto; +} + +/* 페이지네이션 */ +.paginationBox { + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + gap: 0 4px; +} + +.paginationBox .pagination { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0 4px; +} + +.paginationBox .pageLink { + padding-top: 2px; +} + +.paginationBox a { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50px; + color: #6B7280; + border: 1px solid #E5E7EB; + font-weight: 600; +} + +.paginationBox .pageLinkPrev::before { + content: ''; + width: 6px; + height: 10px; + background: url(/public/images/arrow_prev.svg)no-repeat center; +} + +.paginationBox .pageLinkNext::before { + content: ''; + width: 6px; + height: 10px; + background: url(/public/images/arrow_next.svg)no-repeat center; +} + +.paginationBox .active { + background-color: var(--blue); + color: var(--white01); + border: 0; +} + +/* 상세페이지 */ +.detail { + padding-bottom: 40px; + display: flex; + gap: 0 24px; + border-bottom: 1px solid #E5E7EB; +} + +.detail>.imgBox { + width: 486px; + height: 486px; + border-radius: 40PX; + overflow: hidden; + flex-shrink: 0; + box-shadow: 0 0 10px rgba(0, 0, 0, .1); +} + +.detail>.imgBox img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; +} + +.detail .textBox { + display: flex; + flex-direction: column; + position: relative; + width: 100%; +} + +.detail .textBox .text { + margin-bottom: 16px; + display: block; + color: var(--gray03); + line-height: 1.6; + font-weight: 600; +} + +.detail .textBox .productTop { + margin-bottom: 24px; + padding-bottom: 16px; + border-bottom: 1px solid #E5E7EB; +} + +.detail .textBox .title { + margin-bottom: 16px; + color: var(--gray02); + font-size: 24px; + font-weight: 600; +} + +.detail .textBox .price { + display: block; + font-size: 40px; + font-weight: 600; + color: var(--gray02); +} + +.detail .textBox .productIntroduction { + margin-bottom: 24px; +} + +.detail .textBox .productIntroduction p { + color: var(--gray03); + line-height: 1.6; +} + +.detail .textBox .productTag { + margin-bottom: 62px; +} + +.detail .textBox .productTagList { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.detail .textBox .productTagList em { + padding: 5px 16px; + border-radius: 50px; + color: var(--gray02); + line-height: 1.6; + background-color: var(--gray04); + font-style: normal; +} + +.detail .textBox .userBox { + margin-top: auto; + display: flex; + align-items: center; + gap: 0 16px +} + +.detail .textBox .userBox .imgBox { + width: 40px; + height: 40px; + flex-shrink: 0; +} + +.detail .textBox .userBox .imgBox img { + display: block; + width: 100%; +} + +.detail .textBox .userBox .textBox .userId { + margin-bottom: 2px; + color: var(--gray03); + font-size: 14px; + font-weight: 500; + line-height: 1.7; +} + +.detail .textBox .userBox .textBox .regDate { + color: var(--gray01); + font-size: 14px; + line-height: 1.7; +} + +.detail .textBox .likeBox { + padding: 6px 15px; + display: flex; + align-items: center; + gap: 0 7px; + border-radius: 50px; + border: 1px solid #E5E7EB; + background-color: var(--white01); +} + +.detail .textBox .likeBox img { + margin-top: 2px; +} + +.detail .textBox .likeBox span { + font-weight: 500; + color: #6B7280; + line-height: 1.6; +} + +@media (max-width: 1199px) { + + .detail { + padding-bottom: 32px; + } + + .detail>.imgBox { + width: 340px; + height: 340px; + } + + .detail .textBox .productTop { + margin-bottom: 16px; + } + + .detail .textBox .productTag { + margin-bottom: 40px; + } + + .detail .textBox .title { + margin-bottom: 8px; + font-size: 20px; + } + + .detail .textBox .price { + font-size: 32px; + } + + .detail .textBox .text { + margin-bottom: 8px; + font-size: 14px; + } + + .detail .textBox .likeBox { + padding: 2px 15px; + } + + .detail .textBox .likeBox img { + width: 20px; + margin: 0; + } + +} + +@media (max-width: 576px) { + + .detail { + padding-bottom: 24px; + flex-direction: column; + } + + .detail>.imgBox { + margin-bottom: 16px; + width: 100%; + border-radius: 12px; + } + + .detail .textBox .title { + font-size: 16px; + } + + .detail .textBox .price { + font-size: 24px; + } + +} + +/* 문의하기 */ +.inquiryBox { + padding: 40px 0 0; + margin-bottom: 100px; +} + +.inquiryBox .text { + display: block; + margin-bottom: 9px; + color: #111827; + line-height: 1.6; + font-weight: 600; +} + +.inquiryBox form textarea { + margin-bottom: 16px; + padding: 16px 24px; + width: 100%; + height: 104px; + overflow-y: auto; + background-color: var(--gray04); + border-radius: 12px; + border: 0; + font-size: 16px; + font-family: 'pretendard', sans-serif; +} + +.inquiryBox form textarea::placeholder { + color: var(--gray01); +} + +.inquiryBox form .btnBox { + display: flex; + justify-content: flex-end; +} + +/* 댓글 */ +.inquiryInfoList .box .comment { + margin-bottom: 24px; + font-size: 14px; + line-height: 1.7; + color: var(--gray02); +} + +.inquiryInfoList .box { + position: relative; + padding: 24px 0 12px; + border-bottom: 1px solid #E5E7EB; +} + +.inquiryInfoList .box .productInquiryInfo { + display: flex; + align-items: center; + gap: 0 8px; +} + +.inquiryInfoList .box .selectBox { + position: absolute; + top: 29px; + right: 10px; +} + +.inquiryInfoList .box .selectBox .selectBtn { + display: block; + width: 3px; + height: 13px; + background: url(/public/images/setting_icon.svg)no-repeat center; +} + +.inquiryInfoList .box .selectBox .selectBtn.active+.view { + opacity: 1; + z-index: 999; +} + +.inquiryInfoList .box .selectBox .view { + opacity: 0; + position: absolute; + top: 29px; + right: -10px; + width: 139px; + height: 92px; + border-radius: 8px; + border: 1px solid #D1D5DB; + background-color: var(--white01); + overflow: hidden; + z-index: -999; + transition: all .3s; +} + +.inquiryInfoList .box .selectBox .view .select { + display: block; + width: 100%; + height: 46px; + color: #6B7280; + text-align: center; + background-color: var(--white01); + font-size: 14px; +} + +.inquiryInfoList .box .selectBox .view .select:hover { + font-weight: 500; +} + +.inquiryInfoList .box .productInquiryInfo .imgBox { + width: 32px; + height: 32px; + border-radius: 50%; + overflow: hidden; +} + +.inquiryInfoList .box .productInquiryInfo .imgBox img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; +} + +.inquiryInfoList .box .productInquiryInfo .textBox .writer { + margin-bottom: 4px; + font-size: 12px; + color: var(--gray03); + line-height: 1.5; +} + +.inquiryInfoList .box .productInquiryInfo .textBox .time { + font-size: 12px; + color: var(--gray03); + line-height: 1.5; +} + +.inquiryInfoList .noInquiry { + margin-top: 10px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + line-height: 1.6; + color: var(--gray01); +} + +.inquiryInfoList .noInquiry::before { + content: ''; + width: 196px; + height: 196px; + background: url(/public/images/noinquiry_img.svg)no-repeat center; + background-size: cover; +} + +@media (max-width: 1199px) { + + .inquiryInfoList .box .selectBox .view { + width: 102px; + } + +} + +/* 공통-버튼 */ +.productDetailBox .btnBox { + display: flex; + justify-content: center; +} + +.productDetailBox .btnBox a { + position: relative; + padding: 11px 40px; + display: block; + border-radius: 50px; + background-color: var(--gray01); + overflow: hidden; +} + +.productDetailBox .btnBox a span { + display: flex; + align-items: center; + gap: 0 8px; + position: relative; + font-size: 18px; + font-weight: 600; + color: var(--gray04); + line-height: 1.4; +} + +.productDetailBox .btnBox a span::after { + content: ''; + margin-top: 3px; + width: 20px; + height: 18px; + background: url(/public/images/return_icon.svg)no-repeat center; +} + +.productDetailBox .btnBox a::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 0; + bottom: 0; + transition: all .3s; + background-color: var(--blue); + border-radius: 50px; + +} + +.productDetailBox .btnBox a:hover::before { + width: 100%; + +} \ No newline at end of file diff --git a/src/components/Pagination.jsx b/src/components/Pagination.jsx new file mode 100644 index 000000000..ea2d991b0 --- /dev/null +++ b/src/components/Pagination.jsx @@ -0,0 +1,64 @@ +import { Link } from "react-router-dom"; + +export default function Pagination({ currentPage, totalPage, setPage }) { + + const pageNumber = []; + for (let i = 1; i <= totalPage; i++) { + pageNumber.push(i); + } + + const btnRange = 5 //보여질 버튼의 개수 + const currentSet = Math.ceil(currentPage / btnRange); //현재 버튼이 몇 번째 세트인 지 나타내는 수 + const startPage = (currentSet - 1) * btnRange + 1; //현재 페이지에 보여지는 버튼의 첫번째 수 + const endPage = Math.min(startPage + btnRange - 1, totalPage); // 현재 세트의 마지막 페이지 번호 + + // 이전 페이지로 이동 + const handlePrev = () => { + if (currentPage > 1) { + setPage(currentPage - 1); + } + }; + + // 다음 페이지로 이동 + const handleNext = () => { + if (currentPage < totalPage) { + setPage(currentPage + 1); + } + }; + + // 페이지 번호 클릭 시 + const handlePageChange = (page) => { + setPage(page); + }; + + return ( + +
+ + 1페이지 뒤로 이동 + + + + + + 1페이지 앞으로 이동 + +
+ + ) + +} \ No newline at end of file diff --git a/src/components/ProductBestList.jsx b/src/components/ProductBestList.jsx new file mode 100644 index 000000000..8c3063e22 --- /dev/null +++ b/src/components/ProductBestList.jsx @@ -0,0 +1,72 @@ +import { useEffect, useState } from "react"; +import axios from "axios"; +import { Link } from "react-router-dom"; + +export default function ProductBestList() { + + const [bestItem, setBestItem] = useState([]); + const noImage = 'https://via.placeholder.com/222?text=No+Image'; + + useEffect(() => { + + async function bestItems() { + try { + let allItems = []; + const totalPages = Math.ceil(175 / 10); + + for (let page = 1; page <= totalPages; page++) { + const response = await axios.get(`https://panda-market-api.vercel.app/products?page=${page}&limit=10`); + allItems = allItems.concat(response.data.list); + } + + const sortedItem = allItems.sort((a, b) => b.favoriteCount - a.favoriteCount); + const likeItem = sortedItem.slice(0, 4); + + setBestItem(likeItem); + + } catch (error) { + console.log(error); + } + }; + + bestItems() + + }, []) + + + return ( +
+ +
+ ) + +} \ No newline at end of file diff --git a/src/components/ProductComment.jsx b/src/components/ProductComment.jsx new file mode 100644 index 000000000..6656945fd --- /dev/null +++ b/src/components/ProductComment.jsx @@ -0,0 +1,86 @@ +import { useEffect, useState } from "react" +import axios from "axios"; + +export default function ProductComment({ productId }) { + + const [ProductComment, setProductComment] = useState([]); + const [selectViewToggle, setSelectViewToggle] = useState(''); + const limit = 10; // 페이지당 댓글 수 (기본값 예시) + //const [cursor, setCursor] = useState(null); 다음 페이지를 위한 커서 + + function selectToggle(commentId) { + setSelectViewToggle(selectViewToggle === commentId ? '' : commentId); + } + + useEffect(() => { + + async function ProductInquiryInfo() { + try { + const response = await axios.get(`https://panda-market-api.vercel.app/products/${productId}/comments`, { + params: { + limit: limit, // 페이지당 댓글 수 + }, + }); + setProductComment(response.data.list); + } catch (error) { + console.error("error:", error); + } + } + + ProductInquiryInfo(); + + }, [productId]) + + return ( + +
+ { + ProductComment.length > 0 ? ( + ProductComment.map((data, i) => { + return ( +
+

+ {data.content} +

+
+
+ {data.writer.nickname} +
+
+

+ {data.writer.nickname} +

+ + {new Date(data.updatedAt).toLocaleDateString()} + +
+
+
+ + { + selectViewToggle === data.id && ( +
+ + +
+ ) + } +
+
+ ) + }) + ) : ( +

+ 아직 문의가 없어요 +

+ ) + } +
+ + ) + +} \ No newline at end of file diff --git a/src/components/ProductDetail.jsx b/src/components/ProductDetail.jsx new file mode 100644 index 000000000..473d7dab0 --- /dev/null +++ b/src/components/ProductDetail.jsx @@ -0,0 +1,75 @@ + +export default function ProductDetail({ productDetail }) { + + if (!productDetail) { + return
상품을 찾을 수 없습니다.
; + } + + return ( +
+
+ {productDetail.name} +
+
+
+

+ {productDetail.name} +

+ + {productDetail.price}원 + +
+ +
+ + 상품소개 + +

+ {productDetail.description} +

+
+ +
+ + 상품태그 + +
+ + { + (productDetail.tags || []).map((tag, i) => { + return ( + + #{tag} + + ) + }) + } + +
+
+ +
+
+ +
+
+

+ {productDetail.ownerNickname} +

+

+ {new Date(productDetail.createdAt).toLocaleDateString()} +

+
+
+ 하트 아이콘 + + {productDetail.favoriteCount} + +
+
+ +
+
+ ) + +} \ No newline at end of file diff --git a/src/components/ProductInquiry.jsx b/src/components/ProductInquiry.jsx new file mode 100644 index 000000000..e36630e36 --- /dev/null +++ b/src/components/ProductInquiry.jsx @@ -0,0 +1,39 @@ +import { useState } from "react" +import ProductComment from "./ProductComment"; + +export default function ProductInquiry({ productId }) { + + const disabled = useState('disabled'); + + return ( +
+ + 문의하기 + + +
+ +