diff --git a/netlify.toml b/netlify.toml index 28a726ef7..e96a34035 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,5 +1,5 @@ [build] - command = "npm run build" + command = "CI=false npm run build" publish = "build" [[redirects]] from = "/*" diff --git a/package-lock.json b/package-lock.json index 6245c1335..f62b29613 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,11 +11,16 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "@types/jest": "^29.5.14", + "@types/node": "^22.10.5", + "@types/react": "^19.0.4", + "@types/react-dom": "^19.0.2", "axios": "^1.7.9", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^7.0.2", "react-scripts": "5.0.1", + "typescript": "^4.9.5", "web-vitals": "^2.1.4" }, "devDependencies": { @@ -3800,6 +3805,26 @@ "node": ">=12" } }, + "node_modules/@testing-library/react/node_modules/@types/react": { + "version": "18.3.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", + "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@testing-library/react/node_modules/@types/react-dom": { + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", + "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, "node_modules/@testing-library/react/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -4078,9 +4103,10 @@ } }, "node_modules/@types/jest": { - "version": "29.5.4", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.4.tgz", - "integrity": "sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A==", + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "license": "MIT", "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" @@ -4329,9 +4355,13 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "node_modules/@types/node": { - "version": "20.5.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz", - "integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==" + "version": "22.10.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz", + "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -4344,9 +4374,11 @@ "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==" }, "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "license": "MIT", + "peer": true }, "node_modules/@types/q": { "version": "1.5.6", @@ -4364,21 +4396,21 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "node_modules/@types/react": { - "version": "18.2.21", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", - "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.4.tgz", + "integrity": "sha512-3O4QisJDYr1uTUMZHA2YswiQZRq+Pd8D+GdVFYikTutYsTz+QZgWkAPnP7rx9txoI6EXKcPiluMqWPFV3tT9Wg==", + "license": "MIT", "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", - "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", - "dependencies": { - "@types/react": "*" + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.2.tgz", + "integrity": "sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" } }, "node_modules/@types/resolve": { @@ -4394,11 +4426,6 @@ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" }, - "node_modules/@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" - }, "node_modules/@types/semver": { "version": "7.5.1", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", @@ -5895,9 +5922,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001528", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001528.tgz", - "integrity": "sha512-0Db4yyjR9QMNlsxh+kKWzQtkyflkG/snYheSzkjmvdEtEXB1+jt7A2HmSEiO6XIJPIbo92lHNGNySvE5pZcs5Q==", + "version": "1.0.30001692", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", + "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==", "funding": [ { "type": "opencollective", @@ -5911,7 +5938,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/case-sensitive-paths-webpack-plugin": { "version": "2.4.0", @@ -16752,7 +16780,7 @@ "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "peer": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16780,6 +16808,12 @@ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", diff --git a/package.json b/package.json index 11437aad4..b0b34e32a 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,16 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "@types/jest": "^29.5.14", + "@types/node": "^22.10.5", + "@types/react": "^19.0.4", + "@types/react-dom": "^19.0.2", "axios": "^1.7.9", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^7.0.2", "react-scripts": "5.0.1", + "typescript": "^4.9.5", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/src/App.js b/src/App.js index d953960ff..949e802ce 100644 --- a/src/App.js +++ b/src/App.js @@ -1,9 +1,8 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom'; -import Home from './components/page/Home'; -import ProductListPage from './components/page/ProductListPage'; -import ProductRegPage from './components/page/ProductRegPage'; -import ProductDetailPage from './components/page/ProductDetailPage'; - +import Home from './pages/Home'; +import ProductListPage from './pages/ProductListPage'; +import ProductRegPage from './pages/ProductRegPage'; +import ProductDetailPage from './pages/ProductDetailPage'; function App() { return ( @@ -12,7 +11,7 @@ function App() { } /> } /> } /> - } /> + } /> ); diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 000000000..bf9afd243 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,20 @@ +import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import Home from './pages/Home'; +import ProductListPage from './pages/ProductListPage'; +import ProductRegPage from './pages/ProductRegPage'; +import ProductDetailPage from './pages/ProductDetailPage'; + +function App() { + return ( + + + } /> + } /> + } /> + } /> + + + ); +} + +export default App; diff --git a/src/asset/css/style.css b/src/asset/css/style.css index d1e82539a..23c3d4ba0 100644 --- a/src/asset/css/style.css +++ b/src/asset/css/style.css @@ -11,7 +11,10 @@ --red: #f74747 } -/* 에러메세지 */ +main { + margin-top: 70px; +} + .errorText { margin: 22px 0 0; font-size: 16px; @@ -19,651 +22,9 @@ color: var(--red); } -/* 공통-가운데영역 */ -.container { - max-width: 1120px; - margin: 0 auto; -} - -@media (max-width: 1199px) { - - .container { - padding: 0 24px; - } - -} - -@media (max-width: 767px) { - - .container { - padding: 0 16px; - } - -} - -/* 공통-레이아웃 */ -main { - margin-top: 70px; -} - -p.title { - font-weight: 700; - font-size: 40px; - line-height: 1.4; -} - -.infoBox { - display: flex; - height: 444px; - border-radius: 12px; - overflow: hidden; -} - -.infoBox .imgBox { - flex-shrink: 0; - width: 580px; -} - -.infoBox .imgBox img { - display: block; - width: 100%; - height: 100%; - object-fit: cover; -} - -.infoBox .infoText { - display: block; - margin-bottom: 12px; - color: #3692FF; - font-size: 18px; - line-height: 1.44; - font-weight: 700; -} - -.infoBox .textBox { - width: 100%; - display: flex; - flex-direction: column; - justify-content: center; - padding: 0 21px 0 52px; - background-color: #FCFCFC; -} - -.infoBox .textBox p.title { - margin-bottom: 24px; -} - -.infoBox .textBox p.text { - font-size: 24px; - line-height: 1.33; - font-size: 24px; - font-weight: 500; -} - -@media (max-width: 1199px) { - - .infoBox { - flex-direction: column; - height: auto; - } - - .infoBox .imgBox { - width: 100%; - } - - .infoBox .textBox p.title { - font-size: 32px; - } - - .infoBox .textBox p.title br { - display: none; - } - - .infoBox .textBox p.text { - font-size: 18px; - line-height: 1.56; - } - - .infoBox .textBox { - padding: 24px 0 0 0; - } - -} - -@media (max-width: 767px) { - - .infoBox .infoText { - margin-bottom: 8px; - font-size: 16px; - } - - .infoBox .textBox p.title { - margin-bottom: 16px; - font-size: 24px; - } - - .infoBox .textBox p.text { - font-size: 16px; - } - -} - -/* 공통-영역 */ -#productSection01 .container, -#productSection02 .container, -#productSection03 .container { - max-width: 988px; -} - -#productSection01, -#productSection02, -#productSection03 { - padding: 138px 0; -} - -@media (max-width: 1199px) { - - #productSection01, - #productSection02, - #productSection03 { - padding: 26px 0; - background-color: #fcfcfc; - } - -} - -@media (max-width: 767px) { - - #productSection01, - #productSection02, - #productSection03 { - padding: 20px 0; - } - -} - -/* 공통-헤더 */ -#header { - position: fixed; - top: 0; - left: 0; - right: 0; - height: 70px; - background-color: #fff; - border-bottom: 1px solid #DFDFDF; - z-index: 99; -} - -#header nav { - height: 100%; - display: flex; - align-items: center; -} - -#header nav .depth01 { - display: flex; - align-items: center; -} - -#header nav .depth01 li h2 a { - display: block; - padding: 24px 15px; - font-size: 18px; - font-weight: 700; - color: var(--gray03); - transition: all .3s; -} - -#header nav .depth01 li h2 a.active { - color: var(--blue); -} - -#header nav .depth01 li h2 a:hover { - color: var(--blue); -} - -#header .header { - position: relative; - height: 70px; - display: flex; - align-items: center; -} - -#header .logo { - margin-right: 32px; -} - -#header .logo h1 a, -#header .logo h1 a img { - display: block; -} - -#header .loginBox { - position: absolute; - top: 50%; - transform: translateY(-50%); - right: 0; -} - -#header .loginBox a { - display: block; - width: 40px; - height: 40px; - background: url(/public/images/user_icon.svg)no-repeat center; - background-size: cover; -} - -/* #header .loginBox a { - display: block; - width: 128px; - height: 48px; - line-height: 48px; - background-color: #3692FF; - border-radius: 8px; - text-align: center; - color: #F3F4F6; - font-weight: 600; - transition: all .3s; -} - -#header .loginBox a:hover { - background-color: #1967D6; -} - */ - -@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; -} - -#footer .footer { - display: flex; - align-items: center; - justify-content: space-between; -} - -#footer .text { - display: block; - color: #9CA3AF; -} - -#footer .footerLink { - display: flex; - align-items: center; - gap: 0 30px; -} - -#footer .footerLink li a { - display: block; - color: #E5E7EB; -} - -#footer .text, -#footer .footerLink, -#footer .snsLink { - flex-shrink: 0; -} - -#footer .text, -#footer .snsLink { - width: 116px; -} - -#footer .snsLink { - display: flex; - justify-content: flex-end; - align-items: center; - gap: 0 13px; -} - -#footer .snsLink li a { - display: block; -} - -#footer .snsLink li a.faceBook { - width: 18px; - height: 18px; - background: url(/public/images/facebook_icon.svg)no-repeat center; - background-size: cover; -} - -#footer .snsLink li a.twitter { - width: 19px; - height: 15px; - background: url(/public/images/twitter_icon.svg)no-repeat center; - background-size: cover; -} - -#footer .snsLink li a.youtube { - width: 20px; - height: 13px; - background: url(/public/images/youtube_icon.svg)no-repeat center; - background-size: cover; -} - -#footer .snsLink li a.insta { - width: 17px; - height: 17px; - background: url(/public/images/insta_icon.svg)no-repeat center; - background-size: cover; -} - -@media (max-width: 767px) { - - #footer .text { - position: absolute; - left: 16px; - bottom: 32px; - } - -} - -/* 영역1 */ -#mainSection { - overflow: hidden; - background-color: #CFE5FF; -} - -#mainSection .container { - max-width: 1110px; -} - -#mainSection .box { - position: relative; - padding: 200px 0 0; - height: 540px; - z-index: 1; -} - -#mainSection .mainContent { - display: flex; - justify-content: space-between; - gap: 0 7px; -} - -#mainSection .text { - padding: 40px 0 0; -} - -#mainSection img { - max-width: 746px; - width: 100%; - display: block; -} - - -#mainSection .title { - margin-bottom: 32px; -} - -#mainSection .view { - display: block; - width: 357px; - height: 56px; - line-height: 58px; - border-radius: 50px; - text-align: center; - background-color: #3692FF; - color: #fff; - transition: all .3s; - font-size: 20px; -} - -#mainSection .view:hover { - background-color: #1967D6; -} - -@media (max-width: 1199px) { - - #mainSection .mainContent { - flex-direction: column; - align-items: center; - } - - #mainSection .mainContent .img { - width: 100%; - padding-top: 211px; - } - - #mainSection .mainContent .img img { - max-width: 100%; - margin: 0 auto; - } - - #mainSection .container { - padding: 0; - } - - #mainSection .box { - padding: 84px 0 0; - height: auto; - } - - #mainSection .text { - padding: 0; - } - - #mainSection .view { - margin: 0 auto; - } - - #mainSection .title { - margin-bottom: 24px; - text-align: center; - } - - #mainSection .title br { - display: none; - } - -} - -@media (max-width: 767px) { - - #mainSection .mainContent .img { - padding-top: 132px; - } - - #mainSection .box { - padding: 48px 0 0; - } - - #mainSection .title { - margin-bottom: 18px; - font-size: 32px; - } - - #mainSection .title br { - display: block; - } - - #mainSection .view { - width: 240px; - height: 48px; - line-height: 48px; - } - -} - -/* 영역2 */ -@media (max-width: 767px) { - - #productSection01 { - padding: 52px 0 20px; - } - -} - -/* 영역3 */ -#productSection02 .infoBox { - flex-direction: row-reverse; -} - -#productSection02 .infoBox .textBox { - padding: 0 64px 0 48px; - text-align: right; -} - -@media (max-width: 1199px) { - - #productSection02 .infoBox { - flex-direction: column; - } - - #productSection02 .infoBox .textBox { - padding: 24px 0 0 0; - } - -} - -/* 영역4 */ -@media (max-width: 1199px) { - - #productSection03 { - padding: 26px 0 56px; - } - -} - -@media (max-width: 767px) { - - #productSection03 { - padding: 20px 0 83px; - } - -} - -/* 영역5 */ -#infoSection { - position: relative; - background-color: #CFE5FF; -} - -#infoSection::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 138px; - background-color: #fcfcfc; -} - -#infoSection .box { - padding: 281px 0 0; - position: relative; - height: 678px; - z-index: 1; -} - -#infoSection img { - display: block; - width: 100%; -} - -#infoSection .text { - flex-shrink: 0; - padding-top: 113px; -} - -#infoSection .layoutBox { - display: flex; - gap: 0 69px; - justify-content: space-between; -} - -@media (max-width: 1199px) { - - #infoSection .box { - padding: 201px 0 0; - height: auto; - } - - #infoSection::after { - display: none; - } - - #infoSection .layoutBox { - flex-direction: column; - } - - #infoSection .text { - margin-bottom: 217px; - padding-top: 0; - text-align: center; - } - -} - -@media (max-width: 767px) { - - #infoSection .box { - padding: 121px 0 0; - } - - #infoSection .text { - margin-bottom: 131px; - } - - #infoSection p.title { - font-size: 32px; - } - -} - -/* 서브-상품페이지 */ -#sub.subProductReg { - background-color: var(--white01); -} - #sub { + padding: 24px 0 100px; + min-height: 1000px; padding: 24px 0 100px; min-height: 1000px; background-color: #fcfcfc; @@ -684,271 +45,6 @@ p.title { line-height: 1.4; } -#sub .cardList.bestCardBox li { - width: 23.59%; -} - -#sub .cardList.allCardBox { - - min-height: 500px; - -} - -#sub .cardList { - display: flex; - flex-wrap: wrap; - gap: 40px 1.87%; -} - -#sub .cardList li { - width: 18.5%; -} - -#sub .cardList li a { - display: block; -} - -#sub .cardList li a:hover .imgBox { - box-shadow: 10px 10px 20px rgba(0, 0, 0, .2); -} - -#sub .cardList li a:hover .imgBox img { - transform: scale(1.2); -} - -#sub .cardList li .imgBox { - position: relative; - padding-top: 100%; - margin-bottom: 16px; - border-radius: 16px; - overflow: hidden; - transition: all .5s; -} - -#sub .cardList li .imgBox img { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - width: 100%; - height: 100%; - object-fit: cover; - transition: all .5s; -} - -#sub .cardList li .textBox { - margin-bottom: 6px; -} - -#sub .cardList li .textBox h4 { - margin-bottom: 6px; - color: var(--gray02); - font-size: 14px; - font-weight: 500; - line-height: 1.7; -} - -#sub .cardList li .textBox .price { - display: block; - font-weight: 700; - line-height: 1.6; - color: var(--gray02); -} - -#sub .cardList li .likeBox { - height: 18px; - display: flex; - align-items: center; - gap: 0 4px; -} - -#sub .cardList li .likeBox span { - display: flex; - align-items: center; - gap: 0 5px; - position: relative; - font-size: 12px; - font-weight: 500; - color: var(--gray03); -} - -#sub .cardList li .likeBox span::before { - content: ''; - width: 14px; - height: 12px; - background: url(/public/images/like_btn.svg)no-repeat center; - background-size: cover; -} - -#sub .productListBox .productBox { - position: relative; - margin-bottom: 49px; -} - -#sub .productListBox .productBox .product { - position: relative; -} - -#sub .productListBox .productBox:last-child { - margin-bottom: 0; -} - -@media (max-width: 1199px) { - - #sub .cardList.bestCardBox li { - width: 49%; - } - - #sub .cardList.allCardBox li { - width: 14%; - } - - #sub .cardList.allCardBox li { - width: 32%; - } - -} - -@media (max-width: 767px) { - - #sub .cardList.bestCardBox { - gap: 20px 0; - } - - #sub .cardList.bestCardBox li { - width: 100%; - } - - #sub .cardList.allCardBox li { - width: 49%; - } - -} - -/* 서브-검색 */ -.searchWrap { - display: flex; - gap: 0 12px; - position: absolute; - top: -9px; - right: 0; - height: 42px; - border-radius: 12px; -} - -.searchWrap .searchBox { - position: relative; - width: 325px; - display: flex; -} - -.searchWrap .searchBox::after { - content: ''; - position: absolute; - top: 13px; - left: 17px; - width: 15px; - height: 15px; - background: url(/public/images/search_icon.svg)no-repeat center center; -} - -.searchWrap .searchBox input { - width: 100%; - height: 42px; - padding: 0 10px 0 44px; - background-color: #F3F4F6; - border-radius: 12px; -} - -.searchWrap .searchBox input::placeholder { - color: var(--gray01); -} - -.searchWrap .btnBox .addBtn { - display: block; - width: 133px; - height: 42px; - line-height: 42px; - border-radius: 8px; - background-color: var(--blue); - text-align: center; - font-weight: 600; - color: #fff; - font-size: 16px; -} - -.searchWrap .selectBox .select { - position: relative; - display: block; - padding: 0 0 0 20px; - width: 130px; - height: 42px; - line-height: 42px; - border: 1px solid #E5E7EB; - border-radius: 12px; - color: var(--gray02); -} - -.searchWrap .selectBox .select::after { - content: ''; - position: absolute; - top: 50%; - transform: translateY(-50%); - right: 24px; - width: 14px; - height: 8px; - background: url(/public/images/arrow_icon.svg)no-repeat center; - background-size: cover; - transition: all .3s; -} - -.searchWrap .selectBox .select.active::after { - transform: translateY(-50%) rotate(-180deg); -} - -.searchWrap .selectBox .select.active+.viewList { - opacity: 1; - z-index: 99; -} - -.searchWrap .selectBox .viewList { - position: relative; - opacity: 0; - margin-top: 8px; - border: 1px solid #E5E7EB; - border-radius: 12px; - overflow: hidden; - z-index: -1; - transition: all .3s; - background-color: var(--white01); -} - -.searchWrap .selectBox .viewList li { - border-bottom: 1px solid #E5E7EB; -} - -.searchWrap .selectBox .viewList li:last-child { - border-bottom: 0; -} - -.searchWrap .selectBox .viewList li a { - text-align: center; - display: block; - height: 42px; - line-height: 42px; - color: var(--gray02); - background-color: #fff; -} - -.searchWrap .selectBox .viewList li a:hover { - font-weight: 500; -} - -.infoTitle { - text-align: center; -} - .loadingText { margin: 150px 0 0; text-align: center; @@ -956,587 +52,4 @@ p.title { color: var(--blue); } -/* 상품등록 */ -.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; -} - -.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/Container.module.css b/src/components/Container.module.css new file mode 100644 index 000000000..8dd3ccd6d --- /dev/null +++ b/src/components/Container.module.css @@ -0,0 +1,21 @@ +.container{ + max-width: 1120px; + margin: 0 auto; + +} + +@media (max-width: 1199px) { + + .container { + padding: 0 24px; + } + +} + +@media (max-width: 767px) { + + .container { + padding: 0 16px; + } + +} \ No newline at end of file diff --git a/src/components/Container.tsx b/src/components/Container.tsx new file mode 100644 index 000000000..18db5447a --- /dev/null +++ b/src/components/Container.tsx @@ -0,0 +1,14 @@ +import { ReactNode } from 'react'; +import style from './Container.module.css'; + +interface ContainerProps { + className?: string; + page?: boolean; + children: ReactNode; +} + +export default function Container({ className = '', page = false, children }: ContainerProps) { + const classNames = `${style.container} ${page ? style.page : '' + } ${className}`; + return
{children}
; +} diff --git a/src/components/CustomSelect.module.css b/src/components/CustomSelect.module.css new file mode 100644 index 000000000..7292bcfcd --- /dev/null +++ b/src/components/CustomSelect.module.css @@ -0,0 +1,66 @@ +.selectBox .select { + position: relative; + display: block; + padding: 0 0 0 20px; + width: 130px; + height: 42px; + line-height: 42px; + border: 1px solid #E5E7EB; + border-radius: 12px; + color: var(--gray02); +} + +.selectBox .select::after { + content: ''; + position: absolute; + top: 50%; + transform: translateY(-50%); + right: 24px; + width: 14px; + height: 8px; + background: url(/public/images/arrow_icon.svg)no-repeat center; + background-size: cover; + transition: all .3s; +} + +.selectBox .select.active::after { + transform: translateY(-50%) rotate(-180deg); +} + +.selectBox .select.active + .viewList { + opacity: 1; + z-index: 99; +} + +.selectBox .viewList { + position: relative; + opacity: 0; + margin-top: 8px; + border: 1px solid #E5E7EB; + border-radius: 12px; + overflow: hidden; + z-index: -1; + transition: all .3s; + background-color: var(--white01); +} + +.selectBox .viewList li { + border-bottom: 1px solid #E5E7EB; +} + +.selectBox .viewList li:last-child { + border-bottom: 0; +} + +.selectBox .viewList li a { + text-align: center; + display: block; + height: 42px; + line-height: 42px; + color: var(--gray02); + background-color: #fff; +} + +.selectBox .viewList li a:hover { + font-weight: 500; +} \ No newline at end of file diff --git a/src/components/CustomSelect.tsx b/src/components/CustomSelect.tsx new file mode 100644 index 000000000..57eb3ecf1 --- /dev/null +++ b/src/components/CustomSelect.tsx @@ -0,0 +1,46 @@ +import { Link } from "react-router-dom"; +import style from "./CustomSelect.module.css" + +interface CustomSelectProps { + + selectView: boolean; + toggleSelect: (e: React.MouseEvent) => void; + displaySortOrder: string; + handleSortChange: (newSortOrder: string) => void; + +} + +export default function CustomSelect({ selectView, toggleSelect, displaySortOrder, handleSortChange }: CustomSelectProps) { + + return ( +
+ + {displaySortOrder} + + +
    +
  • + handleSortChange('recent')} + > + 최신순 + +
  • +
  • + handleSortChange('favorite')} + > + 좋아요순 + +
  • +
+
+ ) + +} \ No newline at end of file diff --git a/src/components/Pagination.module.css b/src/components/Pagination.module.css new file mode 100644 index 000000000..32daf046b --- /dev/null +++ b/src/components/Pagination.module.css @@ -0,0 +1,51 @@ +.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; +} \ No newline at end of file diff --git a/src/components/Pagination.tsx b/src/components/Pagination.tsx new file mode 100644 index 000000000..7ea3d82c3 --- /dev/null +++ b/src/components/Pagination.tsx @@ -0,0 +1,73 @@ +import { Link } from "react-router-dom"; +import style from "./Pagination.module.css" + +interface PaginationProps{ + + currentPage: number; + totalPage: number; + setPage: React.Dispatch>; + +} + +export default function Pagination({ currentPage, totalPage, setPage }: PaginationProps) { + + 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:number) => { + setPage(page); + }; + + return ( + +
+ + 1페이지 뒤로 이동 + + +
+ { + // startPage부터 endPage까지 페이지 버튼 생성 + Array.from({ length: endPage - startPage + 1 }, (_, i) => startPage + i).map((page) => ( + handlePageChange(page)} + > + {page} + + )) + } +
+ + + 1페이지 앞으로 이동 + +
+ + ) + +} \ No newline at end of file diff --git a/src/components/ProductBestList.module.css b/src/components/ProductBestList.module.css new file mode 100644 index 000000000..f2eac9bfe --- /dev/null +++ b/src/components/ProductBestList.module.css @@ -0,0 +1,110 @@ +.cardList { + display: flex; + flex-wrap: wrap; + gap: 40px 1.87%; +} + +.cardList li { + width: 18.5%; +} + +.cardList li a { + display: block; +} + +.cardList li a:hover .imgBox { + box-shadow: 10px 10px 20px rgba(0, 0, 0, .2); +} + +.cardList li a:hover .imgBox img { + transform: scale(1.2); +} + +.cardList li .imgBox { + position: relative; + padding-top: 100%; + margin-bottom: 16px; + border-radius: 16px; + overflow: hidden; + transition: all .5s; +} + +.cardList li .imgBox img { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + width: 100%; + height: 100%; + object-fit: cover; + transition: all .5s; +} + +.cardList li .textBox { + margin-bottom: 6px; +} + +.cardList li .textBox h4 { + margin-bottom: 6px; + color: var(--gray02); + font-size: 14px; + font-weight: 500; + line-height: 1.7; +} + +.cardList li .textBox .price { + display: block; + font-weight: 700; + line-height: 1.6; + color: var(--gray02); +} + +.cardList li .likeBox { + height: 18px; + display: flex; + align-items: center; + gap: 0 4px; +} + +.cardList li .likeBox span { + display: flex; + align-items: center; + gap: 0 5px; + position: relative; + font-size: 12px; + font-weight: 500; + color: var(--gray03); +} + +.cardList li .likeBox span::before { + content: ''; + width: 14px; + height: 12px; + background: url(/public/images/like_btn.svg)no-repeat center; + background-size: cover; +} + +.cardList.bestCardBox li { + width: 23.59%; +} + +@media (max-width: 1199px) { + + .cardList.bestCardBox li { + width: 49%; + } + +} + +@media (max-width: 767px) { + + .cardList.bestCardBox { + gap: 20px 0; + } + + .cardList.bestCardBox li { + width: 100%; + } + +} \ No newline at end of file diff --git a/src/components/ProductBestList.tsx b/src/components/ProductBestList.tsx new file mode 100644 index 000000000..a6e73e213 --- /dev/null +++ b/src/components/ProductBestList.tsx @@ -0,0 +1,73 @@ +import { useEffect, useState } from "react"; +import axios from "axios"; +import { Link } from "react-router-dom"; +import style from "./ProductBestList.module.css" + +interface BestItemProps{ + + id: number; + images: string[]; + name: string; + price: number; + favoriteCount: number; + +} + +export default function ProductBestList() { + + const [bestItem, setBestItem] = useState([]); + const noImage = 'https://via.placeholder.com/222?text=No+Image'; + + async function getBestItems() { + + try { + + const response = await axios.get('https://panda-market-api.vercel.app/products?page=1&pageSize=4&orderBy=favorite') + const result = response.data.list; + + setBestItem(result); + + } catch (error) { + console.log('error :', error); + } + + }; + + useEffect(() => { + getBestItems() + }, []) + + return ( +
    + { + bestItem.map((data, i) => { + return ( +
  • + +
    + 0 ? data.images[0] : noImage} alt={data.name} /> +
    + +
    +

    + {data.name} +

    +

    + {data.price} +

    +
    + +
    + + {data.favoriteCount} + +
    + +
  • + ) + }) + } +
+ ) + +} \ No newline at end of file diff --git a/src/components/ProductComment.module.css b/src/components/ProductComment.module.css new file mode 100644 index 000000000..3fba2730f --- /dev/null +++ b/src/components/ProductComment.module.css @@ -0,0 +1,119 @@ + +.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; + } + +} \ No newline at end of file diff --git a/src/components/ProductComment.tsx b/src/components/ProductComment.tsx new file mode 100644 index 000000000..aaa18f37e --- /dev/null +++ b/src/components/ProductComment.tsx @@ -0,0 +1,98 @@ +import { useEffect, useState } from "react" +import axios from "axios"; +import style from "./ProductComment.module.css" + +interface ProductCommentProps { + id: number; + productId: number; + content: string; + writer: { + image?: string; + nickname: string; + }; + updatedAt: string; + +} + +export default function ProductComment({ productId }: ProductCommentProps) { + + const [ProductComment, setProductComment] = useState([]); + const [selectViewToggle, setSelectViewToggle] = useState(''); + const limit = 10; // 페이지당 댓글 수 (기본값 예시) + + function selectToggle(commentId: string) { + setSelectViewToggle(selectViewToggle === commentId ? '' : commentId); + } + + 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); + } + } + + useEffect(() => { + + ProductInquiryInfo(); + + }, [productId]) + + return ( + +
+ { + ProductComment.length > 0 ? ( + ProductComment.map((data, i) => { + return ( +
+

+ {data.content} +

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

+ {data.writer.nickname} +

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

+ 아직 문의가 없어요 +

+ ) + } +
+ + ) + +} \ No newline at end of file diff --git a/src/components/ProductDetail.module.css b/src/components/ProductDetail.module.css new file mode 100644 index 000000000..a48ff94e7 --- /dev/null +++ b/src/components/ProductDetail.module.css @@ -0,0 +1,204 @@ +.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; + } + +} \ No newline at end of file diff --git a/src/components/ProductDetail.tsx b/src/components/ProductDetail.tsx new file mode 100644 index 000000000..519fd7e0b --- /dev/null +++ b/src/components/ProductDetail.tsx @@ -0,0 +1,91 @@ + +import style from "./ProductDetail.module.css" + +interface ProductDetailProps { + productDetail: { + id: number; + name: string; + description: string; + images: string; + price: number; + tags: string[]; + ownerNickname:string; + favoriteCount:number; + createdAt:number; + }; +} + +export default function ProductDetail({ productDetail }: ProductDetailProps) { + + 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 index e36630e36..178f94704 100644 --- a/src/components/ProductInquiry.jsx +++ b/src/components/ProductInquiry.jsx @@ -1,9 +1,4 @@ -import { useState } from "react" -import ProductComment from "./ProductComment"; - -export default function ProductInquiry({ productId }) { - - const disabled = useState('disabled'); +export default function ProductInquiry() { return (
@@ -17,22 +12,17 @@ export default function ProductInquiry({ productId }) {