From f9fd79fa8562da941231f7cf1a7d36b7192c332f Mon Sep 17 00:00:00 2001 From: ramong26 Date: Wed, 21 May 2025 12:02:28 +0900 Subject: [PATCH 1/5] =?UTF-8?q?style:=20sass=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?,=20=EB=A1=9C=EA=B7=B8=EC=9D=B8/=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=B0=BD=EA=B9=8C=EC=A7=80=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.ts | 1 + package-lock.json | 409 +++++++++++++++++- package.json | 5 +- public/assets/image/Logo.png | Bin 2223 -> 0 bytes .../image/{ArrowDown.png => arrow_down.png} | Bin .../image/{BestBadge.png => best_badge.png} | Bin .../{HeartActive.png => heart_active.png} | Bin .../{HeartInactive.png => heart_inactive.png} | Bin .../image/{LogoFace.png => logo_face.png} | Bin public/assets/image/logo_text.png | Bin 0 -> 5035 bytes .../image/{NoImage.png => no_image.png} | Bin public/assets/svg/Best_Badge.svg | 6 - public/assets/svg/Delete.svg | 5 - public/assets/svg/Google.svg | 8 +- public/assets/svg/Kakao.svg | 6 +- public/assets/svg/Setting.svg | 5 - .../svg/{ArrowLeft.svg => arrow_left.svg} | 0 .../svg/{ArrowRight.svg => arrow_right.svg} | 0 .../svg/{BackIcon.svg => back_icon.svg} | 0 public/assets/svg/delete_tag.svg | 5 + .../{InquiryEmpty.svg => inquiry_empty.svg} | 0 public/assets/svg/{Plus.svg => plus_icon.svg} | 0 .../svg/{ProfileIcon.svg => profile_icon.svg} | 0 public/assets/svg/setting_icon.svg | 5 + .../assets/svg/{Sort.svg => sort_arrow.svg} | 0 src/app/ClientLayout.tsx | 4 +- .../Home/{page.style.ts => home.module.scss} | 300 +++++++------ src/app/Home/page.tsx | 158 +++---- src/app/Items/BestItems.tsx | 8 +- src/app/Items/RecentItems.tsx | 10 +- .../[productId]/ItemsDetailDescription.tsx | 14 +- .../[productId]/ItemsDetailQuestionArrary.tsx | 20 +- .../ItemsDetailQuestionTextarea.tsx | 10 +- src/app/Items/[productId]/page.tsx | 11 +- src/app/Items/page.tsx | 20 +- src/app/LoginAndSignup/LoginField.tsx | 137 ------ src/app/additem/page.tsx | 16 +- src/app/boards/BestBoards.tsx | 44 +- src/app/boards/BoardDetailArrary.tsx | 20 +- src/app/boards/BoardList.tsx | 95 ++-- src/app/boards/page.tsx | 115 +---- src/app/globals.css | 1 - src/app/hooks/useCommentService.tsx | 4 +- src/app/hooks/useProductService.tsx | 4 +- src/app/login/login.module.scss | 109 +++++ src/app/login/page.tsx | 181 ++------ src/app/page.style.ts | 9 +- src/app/page.tsx | 6 +- .../app/privacy/page.tsx | 0 src/app/signup/page.tsx | 281 ++++++------ src/app/signup/signup.module.scss | 108 +++++ src/app/styles/Home.module.css | 229 ---------- src/{app => components}/common/Button.tsx | 0 .../common/ButtonImageInput.tsx} | 10 +- src/{app => components}/common/DropDown.tsx | 8 +- src/{app => components}/common/Tag.tsx | 6 +- .../common/TextInputPlaceholder.tsx | 4 +- .../LoginAndSignup/LoginField.module.scss | 62 +++ .../domain/LoginAndSignup/LoginField.tsx | 81 ++++ .../domain/Nav}/ItemsNavVar.tsx | 10 +- .../domain}/Nav/NavVArLayout.tsx | 2 +- src/hooks/useDebounce.ts | 10 + src/hooks/useGetArticle.ts | 40 ++ src/hooks/useGetBeatArticle.ts | 24 + src/hooks/useInfiniteScroll.ts | 40 ++ src/{app => lib}/api/client/interceptors.tsx | 0 src/{app => lib}/api/client/requestor.tsx | 0 .../api/service}/articleService.tsx | 2 +- .../api/service}/authService.jsx | 0 .../api/service}/commentService.tsx | 2 +- .../api/service}/imageService.jsx | 0 .../api/service}/productService.tsx | 2 +- .../api/service}/userService.jsx | 0 src/{app => }/styles/GlobalStyles.tsx | 1 - src/{app => }/styles/globals.css | 0 src/{app => }/styles/reset.css | 0 src/{app => }/styles/textStyle.ts | 0 src/{app => }/styles/theme.ts | 0 src/styles/variables.scss | 62 +++ src/{app => }/types/article.ts | 2 +- src/{app => }/types/comment.ts | 0 src/{app => }/types/product.ts | 0 src/{app => }/types/theme.d.ts | 0 src/{app => }/utils/datetime.ts | 0 tsconfig.json | 29 +- 85 files changed, 1576 insertions(+), 1190 deletions(-) delete mode 100644 public/assets/image/Logo.png rename public/assets/image/{ArrowDown.png => arrow_down.png} (100%) rename public/assets/image/{BestBadge.png => best_badge.png} (100%) rename public/assets/image/{HeartActive.png => heart_active.png} (100%) rename public/assets/image/{HeartInactive.png => heart_inactive.png} (100%) rename public/assets/image/{LogoFace.png => logo_face.png} (100%) create mode 100644 public/assets/image/logo_text.png rename public/assets/image/{NoImage.png => no_image.png} (100%) delete mode 100644 public/assets/svg/Best_Badge.svg delete mode 100644 public/assets/svg/Delete.svg delete mode 100644 public/assets/svg/Setting.svg rename public/assets/svg/{ArrowLeft.svg => arrow_left.svg} (100%) rename public/assets/svg/{ArrowRight.svg => arrow_right.svg} (100%) rename public/assets/svg/{BackIcon.svg => back_icon.svg} (100%) create mode 100644 public/assets/svg/delete_tag.svg rename public/assets/svg/{InquiryEmpty.svg => inquiry_empty.svg} (100%) rename public/assets/svg/{Plus.svg => plus_icon.svg} (100%) rename public/assets/svg/{ProfileIcon.svg => profile_icon.svg} (100%) create mode 100644 public/assets/svg/setting_icon.svg rename public/assets/svg/{Sort.svg => sort_arrow.svg} (100%) rename src/app/Home/{page.style.ts => home.module.scss} (54%) delete mode 100644 src/app/LoginAndSignup/LoginField.tsx create mode 100644 src/app/login/login.module.scss rename "src/app/Privacy/Privacy\342\200\231.jsx" => src/app/privacy/page.tsx (100%) create mode 100644 src/app/signup/signup.module.scss delete mode 100644 src/app/styles/Home.module.css rename src/{app => components}/common/Button.tsx (100%) rename src/{app/common/ButtonImage.tsx => components/common/ButtonImageInput.tsx} (93%) rename src/{app => components}/common/DropDown.tsx (93%) rename src/{app => components}/common/Tag.tsx (85%) rename src/{app => components}/common/TextInputPlaceholder.tsx (93%) create mode 100644 src/components/domain/LoginAndSignup/LoginField.module.scss create mode 100644 src/components/domain/LoginAndSignup/LoginField.tsx rename src/{app/common => components/domain/Nav}/ItemsNavVar.tsx (92%) rename src/{app => components/domain}/Nav/NavVArLayout.tsx (92%) create mode 100644 src/hooks/useDebounce.ts create mode 100644 src/hooks/useGetArticle.ts create mode 100644 src/hooks/useGetBeatArticle.ts create mode 100644 src/hooks/useInfiniteScroll.ts rename src/{app => lib}/api/client/interceptors.tsx (100%) rename src/{app => lib}/api/client/requestor.tsx (100%) rename src/{app/api/services => lib/api/service}/articleService.tsx (94%) rename src/{app/api/services => lib/api/service}/authService.jsx (100%) rename src/{app/api/services => lib/api/service}/commentService.tsx (95%) rename src/{app/api/services => lib/api/service}/imageService.jsx (100%) rename src/{app/api/services => lib/api/service}/productService.tsx (95%) rename src/{app/api/services => lib/api/service}/userService.jsx (100%) rename src/{app => }/styles/GlobalStyles.tsx (95%) rename src/{app => }/styles/globals.css (100%) rename src/{app => }/styles/reset.css (100%) rename src/{app => }/styles/textStyle.ts (100%) rename src/{app => }/styles/theme.ts (100%) create mode 100644 src/styles/variables.scss rename src/{app => }/types/article.ts (90%) rename src/{app => }/types/comment.ts (100%) rename src/{app => }/types/product.ts (100%) rename src/{app => }/types/theme.d.ts (100%) rename src/{app => }/utils/datetime.ts (100%) diff --git a/next.config.ts b/next.config.ts index e0bb3e07..ae82c005 100644 --- a/next.config.ts +++ b/next.config.ts @@ -2,6 +2,7 @@ import type { NextConfig } from 'next' const nextConfig: NextConfig = { reactStrictMode: true, + webpack(config, { isServer }) { if (!isServer) { config.resolve.fallback = { fs: false } diff --git a/package-lock.json b/package-lock.json index 784a89f4..30aa0503 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,13 +8,16 @@ "name": "sprintnext", "version": "0.1.0", "dependencies": { + "@babel/runtime": "^7.27.1", "@types/styled-components": "^5.1.34", "axios": "^1.9.0", + "clsx": "^2.1.1", "next": "15.3.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-router-dom": "^7.5.0", - "styled-components": "^6.1.17" + "sass": "^1.89.0", + "styled-components": "^6.1.18" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -35,6 +38,15 @@ "node": ">=0.10.0" } }, + "node_modules/@babel/runtime": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@emnapi/core": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", @@ -910,6 +922,315 @@ "node": ">=12.4.0" } }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1848,7 +2169,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -1981,12 +2302,36 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -3146,7 +3491,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -3603,6 +3948,12 @@ "node": ">= 4" } }, + "node_modules/immutable": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.2.tgz", + "integrity": "sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -3815,7 +4166,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -3859,7 +4210,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "devOptional": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -3884,7 +4235,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -4278,7 +4629,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -4444,6 +4795,13 @@ } } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT", + "optional": true + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4721,7 +5079,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=8.6" }, @@ -4968,6 +5326,19 @@ "node": ">=18" } }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -5181,6 +5552,26 @@ "dev": true, "license": "MIT" }, + "node_modules/sass": { + "version": "1.89.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.0.tgz", + "integrity": "sha512-ld+kQU8YTdGNjOLfRWBzewJpU5cwEv/h5yyqlSeJcj6Yh8U4TDA9UA5FPicqDz/xgRPWRSYIQNiFks21TbA9KQ==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", @@ -5790,7 +6181,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" diff --git a/package.json b/package.json index 1ea366c9..7d924603 100644 --- a/package.json +++ b/package.json @@ -9,13 +9,16 @@ "lint": "next lint" }, "dependencies": { + "@babel/runtime": "^7.27.1", "@types/styled-components": "^5.1.34", "axios": "^1.9.0", + "clsx": "^2.1.1", "next": "15.3.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-router-dom": "^7.5.0", - "styled-components": "^6.1.17" + "sass": "^1.89.0", + "styled-components": "^6.1.18" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/public/assets/image/Logo.png b/public/assets/image/Logo.png deleted file mode 100644 index 19b0c1c0f85d4fcbe2c491cf1fcc8a677de2d90e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2223 zcmV;g2vGNlP)T300009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yP z(7wy|Z`vxP3WDP>8JA-two*@aiadHs1wu!3+Hb2M{o25zJ-*w|`Wb_G+m}=^?EmY% z!!9A;9#RF-9vx}EPGsHwmp`@t*B|NwIkG|<3D5V`N%CZ?qo4Bc+s(A6rl8+aZF%T= zj)<+)?PH%DjBFJs&z!&ktr*wQ$FeBGsK)iGyDSPa!t-ebHlj84`0gbAy(**FX5PF6F%l2ZO}`+4Aj%;aguk;ZK@+$8NA!*WC=Mab8E3$@&{746 zU73Xu0~?}w8>#o?KsVXPyh~YOBR+4bp-8{2jtPNiX8j2MjBYyesx0uYyil5Q4~XHD zr;=gRWo6VxTwq(+rLu?tSb$gv5fH<$n5(SYvVklbFb8r5VgNvaU^(?!C|@Mc?R0x3 zKnwtwf#hghFA$c^2EFNE&_1QsCRQZ`0>l7-8A!IGSFl9BWdPSA0Kfn-000;u1^@s< z)z=Jmwq8vs&~Zbj=WW#`06^u%putPIjfwBZ)s!Fx01&1L000YIivTP@IviaQg*px+ zztDwHWkjd4UtQhZVH|X>;?sB?gB9YU`}Ktz#{Ns zUJ(#@-~G4G)fp9lLW+URyFSH>CJp7$54PA!lL}D7AKY!sxf$mw>?|Szf)Ro}RhW)n zg%Ja3A;ojK7T^1W(JZg|Gv9-B^`tRhifT$=Cbrm*q!MorY)bK>#Tbev1Va7sf!;+ySKQ`rifdWv`ohje4UI zhU1V>WA<(xl4B^aj0AWc?zoI)|4SnVb`_S&(2N391D@k|?s?h7piI+&Nfc8Iv>C<) zEC3;^Y#`m0c(@g;GHC+<86Eu8(LFsOxX1{AqAq&~PR1oI<$=wL46|(eRpXtE`HNskrf=St60IUNQ(Mz3LNL+;gr0K`;2+lDHwgc2dmwoDN zou{O-D@Yn+*b~}-C5tq(Qa%AFGU8@@+|ez$Sp65#S(VS>x~&0-(q8~fpugm3LCJcI zJbIsX`(>q^Bj^YKV1O6^01OZV0DxhtHzC~(HVc>W3TsFJfYLF`wYX0XY}8D0M@S%d zY{8cuuSSm|Jvp=(fO$bUasj!m^c3gROCy-(fS;S98hIqugQDS60e~2+2{HK3ao&7` z?R!(M_&Tl&(MSy60OPMbH;R5MLM@O|i9r<6ndfDny2$plWcbSMOSP!m7d(0}ls}Qc_l7vmqKwyDY$f9N=X%qZnH?VA+nwldD>>Cq#_oNJ6gmbV4=8>+y*HNFe(xdgA!u1MN#CXz?kGCG0vK)KHkWy z-@?4u^QCDkKJSQ7*bzk_SMz|?%uJOosxq*kwXLmynS+(3TF75-l~a!MCuIt01XnloE}cg& zR~-Ut;gVPWd8q$d9#87}RYGr3*nh6hFfv#ZNyR6!<=sqynK6bBiUkj?!+TUMO!X$h zn!z|^CH9!|;7bK5b1&7Oc&-+frRo!(B;6vcO=wk|d>8q@$zoiU)@=Crn&L(fgAr`) zxMu{2r&nzPU!h`gZ_pA+O0JU)Q8)K$Ip2A0BXuDL0JtKZy_aN|W}(=l*0_@E4PLyw zU!zCYvf!uLGoY+UEb?_MI>{&%VgP_6*0r1OXJ+WTV2$?7-b`Ia76|_v&!n|CO(%0l zk+P!?1H=FTS2VrL!)1_1*>zh?NN%U{Tt*Kp4%<@mLR!kxjFH1-Dwz*r0DvR&WDO>< zBK$Dm5kPlYUL*UNr}RY}{0$y4c#58CH5~cKe^ZdIOa?Ikz!8s)C>r-h93PUni-(#6 zW$Ev@46Lyq%Pl=>AW56n!`Y8h#h|8^7DK%B1y=WNwS12g;bU%WH(gS3@O_vhUu3i^vjl|jL6uBH1=gi zi{FDR$vT!nl6@W9V4m-E-t(OI&-XpgoHO5mx1kw4gDX~a<^SipnRt?299T1#mqL}m0u z6Cv8(@E=~pkU^G+(}!q^YHhlfP40q$3*}`@4YKOS_)@M+G*2+Ve!+BizeqS1x9~h( zmfqY1qDUYl(k?bKS!V(OFr9Yu zJ}-jME<)hmq$R+I8vr=8Kmedy5Doy?)Bte$$0zInfR|-?QUUz~UOZrS46cEh`0q`FW*Xv>%pRp= zXuUYpc9{R>XP=E$w9_|_+EP=?pAw14=ecngnLF*Z*>x}v20^{l^{4i`eA1)>bh-=sYXS^E67!S&eI*0o1aG4B60?N4Yv z`~pK`7`mf)d~Cc+bQmjRdM}UmffXe#>Gz?b6Ej@5a!R@X6DY^S02x+aon75N>cQ82 z%H27xb4>t@F50fG;<$na8^!{(_^$#UB#+MyUS6IVtui(7w~d2L%kPy#{uLEzx3US_ zrW?IABf(RW*8}fqC}`lW9cG>DhGYjpSF$5pv&O%9{IN51iR09|GCM*_Q251M)$*A)CBcGY6zz6Yt z^@sr!xeu&9_E*S~{-ez`(#@)sDKR>;r%|ZggJCiA$i)#?$GaMjLj+gtpC4e(#wOxw z!5wj(nhAZK87ijvGwic%?YY~nv{1hWXPCwG@=QAjT0Pr{gv6^a#i0*wE8PUJoSQSM zYoDg%4*IrGRxezCZR@^PN?`}btp|=I`%paZYDCCzogfT~d;4aw@P>-|p>j3S=Uth~ zU_#VsCb51zGUF}B19=cH%Gk}3Xu0DD)<%YjAtx_%{bnrYy(y3fD+LSDImWx&Ak?G% zRaI`a8n5|hOg!|oS04%$@-BiEN3J`EjSN#{j;Ep!yclCJGGf@FrAuA)DrPa_yhXu~ zwcufXjH#BL+9I6mu4}+Bl zJG$8=pT{HHD(nXYm4{oaQ3$>k!>&FGbM+x{lI}IRpFI3(a;`+g>$@J23nC2wPjidc z{PR<`y^e6tDUM1LWkZE1D6J>B;RH`8t)G+> zVXo_S^|Y5`%-k1F7{|G2N;x-FPoumSMzqXS~Wwo?tv=t(pTZH%kYV)C}U z6n|3HNdS9IF$Y!rB)T!_2pSIh?*K0X00|si3iN<)auY*-Bo4%KK+@N%6nGoI1EOu{z13gfbfS&BNCx;kSAE zMxV`TytkCfPb^inQ)U5D#N2(yA#T{XmE=I%i~j2rSYJ*zj-HC_Tbga==(Ilcm14x= z?6QKYA9>GAD`;0qv>QA|k=819kE+x-fl#QbTm{obsJ;U0bLd9;)iISEa`cpJP9OlR z!0YBHZptsqY2!8|qW|st9~2=2=2~`$N*{wTtu|Q>^}|ysDPoV;%>w5XfRR_3BmEy4CHfua zx*t-p^Kue=$pQedZ$rf_>9r+ZvpH~c!r4O&3J>(pP^NtSE;ak9#mbxW2PYJbii?Ii zEdCmdKP~{@y?0@osCsZC>_K5-D_v@^J#g6^0$|Rx-dYxfb3})%?^|4WF*&7x4cr^saL`LWqiC||`r%e6X0EANzIb_JsyndBpcvMt^m!kt05D;$x z+n)E67{xa?x0DT(hI4(L`H1R;^J|3JQP9gg1Xz~xW} z+l%$b$^FDzbcPzwW7MxucC`m(< z_e97*0p;@(fL&Ikjt-o%s0MPngD(`_V!`r zQYeuL^j&GB5*wXSc>*9KQvUTeekdH{`crty&Zq^_eQ0zu$M=S3!d!xgGxzD+2c#K- z5xM02KMR87NiB<2G6SQ}4JF6VdmP+yO^J#qg5YDfd^z#(y?UU0x4}^~z=#{L6IY_UNS@j&m#t>#8sk8SD+#IfyxWlcgvVs9Ld6TnXQk%UW) zyBhfXjFl_a*t8z0x+`HOznqHTKC`f`)z555V{SWk`PNbP(&Ls1y#wD>{kwy(`i<(1 z%h3js3n7;Kbn?MHYGRcB`oo?9E`)whO`K@bECDF8I5JB#B%*z>Y)a|3qHHn`zDX2?$F;G z_xDa?B=|1mf-5wZ#Xgh0Q+*N~x0dvpk!xC*?!4@ox62&-+$`5)tL>52T1)La+ehaP z+tsq1v~P5Y_|_Ad%J{@Ncwh}~gZ6cACvI;=tJQOML0jk32Mrxis;e|-x>oI!c>+Oh z?_h$mmnGhV?fu5}$rwS6gf?>c-r_^X_85!wYl2DV3Eu8Rz64G8+UvN>7gJ<1yj7Wn zKA+971)hpOg5d9YS1-*`Fi=piV0~{oK9f^gwLiLno~FSp*e+Mt7xNc7@{Ifyqk&LY zFiCQX{;b0F$27#&h+=}CtTbC3O5+{t!@&4RbAc`a&t~gldP(slOW0(E&}tiVkK#eZ}#=b>aht@w-q(D4C&44ay8|>QFy+q)O5eUqFq8& z)PH1OD^*X<{^mC7rZ7N)$*MnUPJ)kbW^#kMXt)6)E-lK_B^DaAq9DL`ZEm>1W6_M@ zkYqBw>jvW^DDkn6seb6dDuKH6&G7toT+nhvySMDX@QV4 ze@LE0hSz*E0hC`?>3P+KFj=B0wSwv34Cu^u%KYOjc7^h8z7s!K0N#)AH>=})MFZb! z)zk|@Sr>{?ur>66FhTSwl8e#Q4z){rhGCJ8^Bym5Wm^)fIUq&4dId04*z~Y}`tTQr z7)yO~OEx^AZj2dh+YH~%T+6UHq8#akEpy$A!Pmj$if_@c;Wm)k)O9 zejo&bq-}+us)!2%?gBL8mg=%qZ)^KQ44>>}pRUT4M*VVMgZ`VC1)ZP(`k zRvw(1p!$zTmA8eDzu^0JC@pnp4LN%y_%Bt>&)G4a5~RwCY_mCod|AY+*p%yN@X4cyU!stXdl1@wSi) zX#a%{lX@f)F#omE)@cH$k?f^JhO^x+<@y1S4BrTj)J1O^W8;G-@^#&e-|KL}H{!?s(U6UTFDc3)nBq zyzRTIju+{nRj1J!-Nb6S6ov<86e~sZ$eVo<7eFp8$X+M#Lqy;--h)F=Z4}3y7;YLKer1qU~zB6R>IGeT00zX&iX9XOtnz3 zoU~MMuXGwyiJibx;hCDDJ)mVS$O?GV@2B3TH#L^ux2T{$WTd^_L0{{{Fm^YDn2fl+ zo??l4S>=+H<}te2bNXG^ZRaADT#Vw{tS?x@28qd0^In1t=6=$O)u!YAgf7)x-vj@; z-z4>=8;3|JGDuidfBmF#CIN%Tu)aE zJSgXSicBAT#+f2dymd5`~(@%O~%lVpeD^cGat3!cFDr z@=xoZy)1QlP_b??f<7IZc~ixVL7%6CH@14Yi&-QYSVi5-u6zFz7V}Xz&kL6tljq1J zv(?}AiyJ{3R@Kj{$A+2Bf=6tbw`DKto^i{u6@6MBEKP7FY8J3uH`%u=UiY+#7UYG$ zMH8gl4eE-%meh5hdG(fb^1QYNL+pslo1vD6CLk&PPS=g~^yaBLR~m)y_$c*L?zsNG zx?U`2V3kOrCFs~=>__o2c6SorX7YaxuBM^%0J;b#47XoZX$GZkr+u3GFNWJ0Hk&*m9HWL?^ zl35{&Yu>Dc^1t_{!kAjpa3&(Wi+PVNJK2POI+B0xk8NL6 zaOeJod&Q2bh%@}AcTK{DlR^W>3>}<^H&w34+x#W>Wt{lYHb*=1Q14MzE@R}9#L}<` zQNksI>e9LG&Z`m;>t>iE9euM!M$W}n(JdZ3M;}|GGey1h#hLvX{H!^N= - - - - - diff --git a/public/assets/svg/Delete.svg b/public/assets/svg/Delete.svg deleted file mode 100644 index f6674f7f..00000000 --- a/public/assets/svg/Delete.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/public/assets/svg/Google.svg b/public/assets/svg/Google.svg index 0aa34364..9a013c32 100644 --- a/public/assets/svg/Google.svg +++ b/public/assets/svg/Google.svg @@ -1,10 +1,10 @@ - + - - + + - + diff --git a/public/assets/svg/Kakao.svg b/public/assets/svg/Kakao.svg index a503e487..e779fa4e 100644 --- a/public/assets/svg/Kakao.svg +++ b/public/assets/svg/Kakao.svg @@ -1,11 +1,11 @@ - - + + - + diff --git a/public/assets/svg/Setting.svg b/public/assets/svg/Setting.svg deleted file mode 100644 index 63a0344c..00000000 --- a/public/assets/svg/Setting.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/public/assets/svg/ArrowLeft.svg b/public/assets/svg/arrow_left.svg similarity index 100% rename from public/assets/svg/ArrowLeft.svg rename to public/assets/svg/arrow_left.svg diff --git a/public/assets/svg/ArrowRight.svg b/public/assets/svg/arrow_right.svg similarity index 100% rename from public/assets/svg/ArrowRight.svg rename to public/assets/svg/arrow_right.svg diff --git a/public/assets/svg/BackIcon.svg b/public/assets/svg/back_icon.svg similarity index 100% rename from public/assets/svg/BackIcon.svg rename to public/assets/svg/back_icon.svg diff --git a/public/assets/svg/delete_tag.svg b/public/assets/svg/delete_tag.svg new file mode 100644 index 00000000..1af75bea --- /dev/null +++ b/public/assets/svg/delete_tag.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/assets/svg/InquiryEmpty.svg b/public/assets/svg/inquiry_empty.svg similarity index 100% rename from public/assets/svg/InquiryEmpty.svg rename to public/assets/svg/inquiry_empty.svg diff --git a/public/assets/svg/Plus.svg b/public/assets/svg/plus_icon.svg similarity index 100% rename from public/assets/svg/Plus.svg rename to public/assets/svg/plus_icon.svg diff --git a/public/assets/svg/ProfileIcon.svg b/public/assets/svg/profile_icon.svg similarity index 100% rename from public/assets/svg/ProfileIcon.svg rename to public/assets/svg/profile_icon.svg diff --git a/public/assets/svg/setting_icon.svg b/public/assets/svg/setting_icon.svg new file mode 100644 index 00000000..dd7ed7f5 --- /dev/null +++ b/public/assets/svg/setting_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/assets/svg/Sort.svg b/public/assets/svg/sort_arrow.svg similarity index 100% rename from public/assets/svg/Sort.svg rename to public/assets/svg/sort_arrow.svg diff --git a/src/app/ClientLayout.tsx b/src/app/ClientLayout.tsx index a504592f..abc71bd9 100644 --- a/src/app/ClientLayout.tsx +++ b/src/app/ClientLayout.tsx @@ -1,8 +1,8 @@ 'use client' import { ThemeProvider } from 'styled-components' -import { theme } from './styles/theme' -import GlobalStyles from './styles/GlobalStyles' +import { theme } from '../styles/theme' +import GlobalStyles from '../styles/GlobalStyles' export default function ClientLayout({ children, diff --git a/src/app/Home/page.style.ts b/src/app/Home/home.module.scss similarity index 54% rename from src/app/Home/page.style.ts rename to src/app/Home/home.module.scss index 04c6fcd8..510a4363 100644 --- a/src/app/Home/page.style.ts +++ b/src/app/Home/home.module.scss @@ -1,91 +1,91 @@ -import styled from 'styled-components' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' +@import '../../styles/variables.scss'; -export const HeaderTop = styled.header` +.header-top { display: flex; justify-content: center; position: sticky; top: 0; background-color: #ffffff; -` + position: sticky; + z-index: 10; +} -export const HeaderNav = styled.nav` +.header-nav { display: flex; justify-content: space-between; align-items: center; width: 100%; padding: 0.6rem 0px; margin: 0 13rem; - @media (max-width: 744px) { + @media (max-width: 1023px) { margin: 0 1.5rem; } - @media (max-width: 375px) { + @media (max-width: 743px) { margin: 0 1rem; } -` +} -export const HeaderLogo = styled.div` +.ghader-logo { width: 100%; height: 100%; display: flex; align-items: center; -` +} -export const ButtonWrapper = styled.div` +.button-wrapper { width: auto; height: auto; - @media (max-width: 744px) { + @media (max-width: 1023px) { width: max-content; } -` +} -export const HeaderLogoFace = styled.img` +.header-logo-face { width: 2.5rem; height: 2.5rem; margin-right: 0.5rem; display: flex; - @media (max-width: 375px) { + @media (max-width: 743px) { width: 0; } -` +} -export const HeaderLogoName = styled.img` +.header-logo-name { width: 6.44rem; height: 2.19rem; display: flex; -` +} -export const HeaderLogin = styled.a` +.header-login { width: 10.16rem; height: 3rem; - background-color: #3692ff; + background-color: $primaryBlue-100; border-radius: 8px; - ${(props) => textStyle(16, 600)(props)} - color: ${theme.colors.SecondaryGray[50]}; + @include apply-font($font-16, 600); + color: $secondaryGray-50; text-decoration: none; display: flex; justify-content: center; align-items: center; - @media (max-width: 744px) { + @media (max-width: 1023px) { width: 9.73rem; } - @media (max-width: 375px) { + @media (max-width: 743px) { width: 9.73rem; } -` +} -export const HeaderMain = styled.div` +.header-main { width: 100%; background-color: #cfe5ff; -` +} -export const HeaderMainContainer = styled.div` +.header-main-container { width: max-content; display: flex; padding: 12.5rem 0px 0px 0px; margin: 0 auto; - @media (max-width: 744px) { + @media (max-width: 1023px) { width: 100%; height: 771px; flex-direction: column; @@ -93,110 +93,110 @@ export const HeaderMainContainer = styled.div` justify-content: space-between; padding: 0; } - @media (max-width: 375px) { + @media (max-width: 743px) { width: 100%; height: auto; img { width: 100%; } } -` +} -export const HeaderMainTitle = styled.div` +.header-main-title { width: auto; margin: auto 0; - @media (max-width: 744px) { + @media (max-width: 1023px) { width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; } - @media (max-width: 375px) { + @media (max-width: 743px) { width: 15rem; margin-bottom: 8.25rem; } -` +} -export const HeaderTitleFont = styled.h1` +.header-title-font { width: 20rem; - ${(props) => textStyle(40, 700)(props)} + @include apply-font($font-40, 700); margin: 0 4rem 2rem 0; - color: ${theme.colors.SecondaryGray[700]}; - @media (max-width: 744px) { + color: $secondaryGray-700; + @media (max-width: 1023px) { margin-top: 84px; width: auto; margin: 84px auto 24px; } - @media (max-width: 375px) { + @media (max-width: 743px) { margin-bottom: 1.125rem; text-align: center; - ${(props) => textStyle(32, 700)(props)} + @include apply-font($font-32, 700); width: 254px; } -` +} -export const HeaderItems = styled.a` +.header-items { width: 7.25rem; - background-color: ${theme.colors.PrimaryBlue[100]}; + background-color: $primaryBlue-100; border-radius: 40px; - ${(props) => textStyle(20, 600)(props)} - color: ${theme.colors.SecondaryGray[50]}; + @include apply-font($font-20, 600); + color: $secondaryGray-50; text-decoration: none; padding: 0.75rem 7.75rem; display: inline-block; - @media (max-width: 375px) { + @media (max-width: 743px) { padding: 0.5rem 4rem; margin-top: 1.13rem; } -` +} /* 메인 */ -export const MainBasic = styled.main` +.main-basic { display: flex; flex-direction: column; align-items: center; - @media (max-width: 744px) { + @media (max-width: 1023px) { margin: 24px; } - @media (max-width: 375px) { + @media (max-width: 743px) { margin: 52px 16px 2.5rem 15px; width: 344px; } -` +} -export const MainTheme = styled.div` +.main-theme { display: flex; background-color: #fcfcfc; margin: 8.63rem auto; - @media (max-width: 744px) { + @media (max-width: 1023px) { width: 100%; flex-direction: column; margin: 0; } -` +} -export const MainPopularSellImage = styled.picture` +.main-popular-sell-image { width: auto; height: 27.75rem; - @media (max-width: 744px) { + @media (max-width: 1023px) { img { width: 100%; height: 32.813rem; } } - @media (max-width: 375px) { + @media (max-width: 743px) { height: 16.187rem; img { width: 100%; height: 100%; } } -` +} -export const MainThemeBasic = styled.div` +.main-theme-basic { width: 18.625rem; display: flex; justify-content: center; @@ -204,89 +204,87 @@ export const MainThemeBasic = styled.div` background-color: #fcfcfc; flex-direction: column; margin: auto 4rem; - @media (max-width: 744px) { + @media (max-width: 1023px) { width: 100%; margin: 0; } - @media (max-width: 375px) { + @media (max-width: 743px) { height: 134px; margin: 1.5rem auto 2.69rem 0; } -` +} -export const MainPopularSellFontTop = styled.p` +.main-popular-sell-font-top { margin: 0 0px 0.75rem 0; - ${(props) => textStyle(18, 700)(props)} - color: ${theme.colors.PrimaryBlue[100]}; - @media (max-width: 744px) { + @include apply-font($font-18, 700); + color: $primaryBlue-100; + @media (max-width: 1023px) { margin: 1.5rem auto 1rem; } - @media (max-width: 375px) { + @media (max-width: 743px) { margin: 0 auto 0.5rem 0rem; - ${(props) => textStyle(16, 700)(props)} + @include apply-font($font-16, 700); } -` +} -export const MainPopularSellFontMiddle = styled.h2` +.main-poular-sell-font-middle { width: 306px; margin: 0 auto 1.5rem 0; - ${(props) => textStyle(40, 700)(props)} - color: ${theme.colors.SecondaryGray[700]}; - @media (max-width: 744px) { - ${(props) => textStyle(32, 700)(props)} - width:100%; - } - @media (max-width: 375px) { - ${(props) => textStyle(24, 700)(props)} + @include apply-font($font-40, 700); + color: $secondaryGray-700; + @media (max-width: 1023px) { + @include apply-font($font-32, 700); + width: 100%; + } + @media (max-width: 743px) { + @include apply-font($font-24, 700); margin-bottom: 1rem; line-height: 2rem; } -` +} -export const MainPopularSellFontBottom = styled.p` - ${(props) => textStyle(24, 500)(props)} - color: ${theme.colors.SecondaryGray[700]}; +.main-popular-sell-font-bottom { + @include apply-font($font-24, 500); + color: $secondaryGray-700; width: 100%; - @media (max-width: 744px) { - ${(props) => textStyle(18, 500)(props)} - width:100%; + @media (max-width: 1023px) { + @include apply-font($font-18, 500); + width: 100%; width: 15.75rem; margin-bottom: 3.25rem; } - @media (max-width: 375px) { + @media (max-width: 743px) { margin: 0; - ${(props) => textStyle(16, 500)(props)} + @include apply-font($font-16, 500); width: 205px; } -` - -export const MainThemeCenter = styled.div` +} +.main-theme-center { display: flex; background-color: #fcfcfc; margin: 8.63rem auto; - @media (max-width: 744px) { + @media (max-width: 1023px) { width: 100%; flex-direction: column-reverse; margin: 0; } - @media (max-width: 375px) { + @media (max-width: 743px) { flex-wrap: wrap-reverse; } -` - -export const MainThemeBasicMiddle = styled(MainThemeBasic)` +} +.main-theme-basic-middle { align-items: flex-end; text-align: right; - @media (max-width: 375px) { + @media (max-width: 743px) { width: 330px; margin-right: 0; } -` +} -export const MainSearchImage = styled.picture` +.main-search-image { width: 579px; height: 444px; - @media (max-width: 744px) { + @media (max-width: 1023px) { width: 100%; height: 100%; margin: 0; @@ -295,106 +293,106 @@ export const MainSearchImage = styled.picture` height: 32.5rem; } } - @media (max-width: 375px) { + @media (max-width: 743px) { height: 16.187rem; img { width: 100%; height: 100%; } } -` +} /* footer */ -export const FooterMainContainer = styled.footer` +.footer-main-container { display: flex; flex-direction: column; align-items: center; -` +} -export const FooterEmpty = styled.div` +.footer-empty { width: 100%; height: 138px; background-color: #fcfcfc; - @media (max-width: 744px) { + @media (max-width: 1023px) { width: 0; height: 0; } -` +} -export const FooterContainer = styled.div` +.footer-container { display: flex; justify-content: center; align-items: center; width: 100%; background-color: #cfe5ff; - @media (max-width: 744px) { + @media (max-width: 1023px) { } - @media (max-width: 375px) { + @media (max-width: 743px) { height: 33.75rem; } -` -export const FooterBackground = styled.div` +} +.footer-background { display: flex; align-items: center; height: 397px; margin-top: 9rem; width: 69.375rem; - @media (max-width: 744px) { + @media (max-width: 1023px) { flex-direction: column; justify-content: space-between; width: 100%; margin-top: 0; height: 100%; } -` +} -export const FooterFont = styled.h2` - ${(props) => textStyle(40, 700)(props)} - @media (max-width: 744px) { +.footer-font { + @include apply-font($font-40, 700); + @media (max-width: 1023px) { margin: 12.562rem auto 13.562rem; width: 20.438rem; text-align: center; line-height: 56px; } - @media (max-width: 375px) { - ${(props) => textStyle(32, 700)(props)} + @media (max-width: 743px) { + @include apply-font($font-32, 700); margin: 121px auto 131px; width: 254px; height: 90px; text-align: center; } -` +} -export const FooterImage = styled.picture` +.footer-image { width: 46.63rem; height: 24.81rem; - @media (max-width: 375px) { + @media (max-width: 743px) { width: 100%; img { width: 100%; } } -` +} -export const FooterNav = styled.div` +.footer-nav { width: 100%; - background-color: ${theme.colors.SecondaryGray[900]}; + background-color: $secondaryGray-900; display: flex; justify-content: center; -` +} -export const FooterNavMain = styled.div` +.footern-nav-main { height: 1.25rem; width: 70rem; display: flex; justify-content: space-between; align-items: center; margin: 2rem 0 6.75rem 0; - @media (max-width: 744px) { + @media (max-width: 1023px) { width: 33.5rem; margin: 2rem 8rem 6.75rem 8rem; } - @media (max-width: 375px) { + @media (max-width: 743px) { margin: 2rem; display: grid; grid-template-areas: @@ -403,14 +401,14 @@ export const FooterNavMain = styled.div` width: 100%; height: 98px; } -` +} -export const Codeit = styled.p` - color: ${theme.colors.SecondaryGray[400]}; - @media (max-width: 744px) { - ${(props) => textStyle(16, 400)(props)} +.codeit { + color: $secondaryGray-400; + @media (max-width: 1023px) { + @include apply-font($font-16, 400); } - @media (max-width: 375px) { + @media (max-width: 743px) { grid-area: ct; font-weight: 400; font-size: 16px; @@ -420,44 +418,44 @@ export const Codeit = styled.p` line-height: 18.4px; text-align: center; } -` +} -export const PrivacyFaq = styled.div` +.privacy-faq { margin-right: 13px; - @media (max-width: 375px) { + @media (max-width: 743px) { grid-area: hd; align-items: center; justify-items: center; margin: 0; width: 12.32rem; } -` +} -export const Social = styled.div` +.social { width: 7.25rem; height: 20px; display: flex; justify-content: space-between; - @image { + image { width: 18px; height: 18px; } -` +} -export const Privacy = styled.div` +.privacy { margin: auto 2rem auto 0; - color: ${theme.colors.SecondaryGray[200]}; + color: $secondaryGray-200; text-decoration: none; - @media (max-width: 375px) { + @media (max-width: 743px) { margin: 0 30px 0 0; } -` +} -export const Faq = styled.div` - color: ${theme.colors.SecondaryGray[200]}; +.faq { + color: $secondaryGray-200; text-decoration: none; margin: auto 0; - @media (max-width: 375px) { + @media (max-width: 743px) { margin-right: 2rem; } -` +} diff --git a/src/app/Home/page.tsx b/src/app/Home/page.tsx index 8a37f74c..3a3ad21a 100644 --- a/src/app/Home/page.tsx +++ b/src/app/Home/page.tsx @@ -2,8 +2,8 @@ import React from 'react' import Link from 'next/link' -import * as S from './page.style' -import Button from '../common/Button' +import styles from './home.module.scss' +import Button from '../../components/common/Button' import Logo from '../../../public/assets/image/Logo.png' import LogoFace from '../../../public/assets/image/LogoFace.png' @@ -21,124 +21,130 @@ import Image from 'next/image' function Home() { return ( <> - - - +
+
판다마켓 로고 사진 판다마켓 로고 사진 - - +
+
- - - - - - - +
+ + +
+
+
+

일상의 모든 물건을 거래해 보세요 - - +

+
- - +
+
판다마켓 백그라운드사진 - - - - - +
+
+
+
+
판다마켓 인기 상품 사진 - - +
+
- Hot item +
+ Hot item +
- +
인기 상품을 확인해 보세요 - +
- +
가장 HOT한 중고거래 물품을 판다 마켓에서 확인해 보세요 - +
- - - - +
+
+
+
- Search +
Search
- +
구매를 원하는 상품을 검색하세요 - +
- +
구매하고 싶은 물품은 검색해서 쉽게 찾아보세요 - +
- - +
+
판다마켓 상품 검색 사진 - - - - +
+
+
+
판다마켓 인기 상품 등록 - - +
+
- Register +
+ Register +
- +
판매를 원하는 상품을 등록하세요 - +
- +
어떤 물건이든 판매하고 싶은  상품을 쉽게 등록하세요 - +
- - - - - - - +
+
+
+
+
+
+
- 믿을 수 있는 판다마켓 중고 거래 +
+ 믿을 수 있는 판다마켓 중고 거래 +
- +
판다마켓 백그라운드사진 - - - - - - ©codeit - 2024 - +
+
+
+
+
+
©codeit - 2024
+
- Privacy Policy +
Privacy Policy
- FAQ +
FAQ
- - +
+
페이스북 로고 사진 @@ -151,10 +157,10 @@ function Home() { 인스타그램 로고 사진 - - - - +
+
+
+
) } diff --git a/src/app/Items/BestItems.tsx b/src/app/Items/BestItems.tsx index d01f1f0c..70ba0298 100644 --- a/src/app/Items/BestItems.tsx +++ b/src/app/Items/BestItems.tsx @@ -2,12 +2,12 @@ import React, { useEffect, useState } from 'react' import Link from 'next/link' -import { GetProductIdTypes } from '../types/product' +import { GetProductIdTypes } from '../../types/product' -import HeartInactive from '../../../public/assets/image/HeartInactive.png' +import HeartInactive from '../../../public/assets/image/heart_inactive.png' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' +import { theme } from '../../styles/theme' +import { textStyle } from '../../styles/textStyle' import styled from 'styled-components' import Image from 'next/image' diff --git a/src/app/Items/RecentItems.tsx b/src/app/Items/RecentItems.tsx index 616ae9a7..f5343761 100644 --- a/src/app/Items/RecentItems.tsx +++ b/src/app/Items/RecentItems.tsx @@ -4,14 +4,14 @@ import React, { useEffect, useState } from 'react' import Link from 'next/link' import Image from 'next/image' -import { GetProductIdTypes } from '../types/product' +import { GetProductIdTypes } from '../../types/product' -import HeartInactive from '../../../public/assets/image/HeartInactive.png' -import NoImage from '../../../public/assets/image/NoImage.png' +import HeartInactive from '../../../public/assets/image/heart_inactive.png' +import NoImage from '../../../public/assets/image/no_image.png' import styled from 'styled-components' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' +import { theme } from '../../styles/theme' +import { textStyle } from '../../styles/textStyle' const RecentItems = ({ products }: { products: GetProductIdTypes[] }) => { const [itemsDisplay, setItemsDisplay] = useState(1) diff --git a/src/app/Items/[productId]/ItemsDetailDescription.tsx b/src/app/Items/[productId]/ItemsDetailDescription.tsx index 493220f4..6ce8f163 100644 --- a/src/app/Items/[productId]/ItemsDetailDescription.tsx +++ b/src/app/Items/[productId]/ItemsDetailDescription.tsx @@ -4,16 +4,16 @@ import React from 'react' import { useParams } from 'next/navigation' import { useGetProductId } from '../../hooks/useProductService' -import Tag from '../../common/Tag' -import { formatDate } from '../../utils/datetime' +import Tag from '../../../components/common/Tag' +import { formatDate } from '../../../utils/datetime' -import Setting from '../../../../public/assets/svg/Setting.svg' -import ProfileIcon from '../../../../public/assets/svg/ProfileIcon.svg' -import HeartInactive from '../../../../public/assets/image/HeartInactive.png' +import Setting from '../../../../public/assets/svg/setting_icon.svg' +import ProfileIcon from '../../../../public/assets/svg/profile_icon.svg' +import HeartInactive from '../../../../public/assets/image/heart_inactive.png' import styled from 'styled-components' -import { theme } from '../../styles/theme' -import { textStyle } from '../../styles/textStyle' +import { theme } from '../../../styles/theme' +import { textStyle } from '../../../styles/textStyle' import Image from 'next/image' const ItemsDetailDescription = () => { diff --git a/src/app/Items/[productId]/ItemsDetailQuestionArrary.tsx b/src/app/Items/[productId]/ItemsDetailQuestionArrary.tsx index 3b3002f9..435227a5 100644 --- a/src/app/Items/[productId]/ItemsDetailQuestionArrary.tsx +++ b/src/app/Items/[productId]/ItemsDetailQuestionArrary.tsx @@ -3,18 +3,18 @@ import React, { useState, useEffect, useRef } from 'react' import Image from 'next/image' -import { PostCommentType } from '../../types/comment' -import Button from '../../common/Button' -import TextInputPlaceholder from '../../common/TextInputPlaceholder' -import commentService from '../../../../src/app/api/services/commentService' -import { diffDate } from '../../utils/datetime' -import { formatDate } from '../../utils/datetime' +import { PostCommentType } from '../../../types/comment' +import Button from '../../../components/common/Button' +import TextInputPlaceholder from '../../../components/common/TextInputPlaceholder' +import commentService from '../../../lib/api/service/commentService' +import { diffDate } from '../../../utils/datetime' +import { formatDate } from '../../../utils/datetime' -import Setting from '../../../../public/assets/svg/Setting.svg' +import Setting from '../../../../public/assets/svg/setting_icon.svg' import styled from 'styled-components' -import { theme } from '../../styles/theme' -import { textStyle } from '../../styles/textStyle' +import { theme } from '../../../styles/theme' +import { textStyle } from '../../../styles/textStyle' interface ItemsDetailQuestionArraryProps { productQuestion: PostCommentType @@ -100,7 +100,7 @@ const ItemsDetailQuestionArrary = ({ {/*이미지가 없을 경우 기본 이미지 적용*/} 유저프로필사진 { const params = useParams() // product 데어터 받기 diff --git a/src/app/Items/[productId]/page.tsx b/src/app/Items/[productId]/page.tsx index cf04b23e..c77df0a8 100644 --- a/src/app/Items/[productId]/page.tsx +++ b/src/app/Items/[productId]/page.tsx @@ -1,17 +1,16 @@ 'use client' import React from 'react' -import ItemsNavVar from '@/app/common/ItemsNavVar' +import ItemsNavVar from '../../../components/domain/Nav/ItemsNavVar' import ItemsDetailDescription from './ItemsDetailDescription' import ItemsDetailQuestionTextarea from './ItemsDetailQuestionTextarea' -import Button from '../../common/Button' - -import BackIcon from '../../../../public/assets/svg/BackIcon.svg' +import Button from '../../../components/common/Button' +import BackIcon from '../../../../public/assets/svg/back_icon.svg' import styled from 'styled-components' -import { theme } from '../../styles/theme' -import { textStyle } from '../../styles/textStyle' +import { theme } from '../../../styles/theme' +import { textStyle } from '../../../styles/textStyle' import Image from 'next/image' const ItemsDetail = () => { diff --git a/src/app/Items/page.tsx b/src/app/Items/page.tsx index 8919356e..9d337cf2 100644 --- a/src/app/Items/page.tsx +++ b/src/app/Items/page.tsx @@ -4,22 +4,22 @@ import React, { useEffect, useState } from 'react' import Image from 'next/image' import { useRouter } from 'next/navigation' -import { GetProductIdTypes } from '../types/product' +import { GetProductIdTypes } from '../../types/product' -import ItemsNavVar from '../common/ItemsNavVar' +import ItemsNavVar from '../../components/domain/Nav/ItemsNavVar' import BestItems from './BestItems' import RecentItems from './RecentItems' -import DropDown from '../common/DropDown' -import productService from '../api/services/productService' -import Button from '../common/Button' +import DropDown from '../../components/common/DropDown' +import productService from '../../lib/api/service/productService' +import Button from '../../components/common/Button' -import Search from '../../../public/assets/svg/Search.svg' -import ArrowLeft from '../../../public/assets/svg/ArrowLeft.svg' -import ArrowRight from '../../../public/assets/svg/ArrowRight.svg' +import Search from '../../../public/assets/svg/search.svg' +import ArrowLeft from '../../../public/assets/svg/arrow_left.svg' +import ArrowRight from '../../../public/assets/svg/arrow_right.svg' import styled, { css } from 'styled-components' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' +import { theme } from '../../styles/theme' +import { textStyle } from '../../styles/textStyle' type SelectOption = { value: string diff --git a/src/app/LoginAndSignup/LoginField.tsx b/src/app/LoginAndSignup/LoginField.tsx deleted file mode 100644 index 911ace5c..00000000 --- a/src/app/LoginAndSignup/LoginField.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import React, { useState } from 'react' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' -import styled from 'styled-components' -import Image from 'next/image' - -const Field = styled.span` - display: inline-block; - margin-bottom: 1.6rem; - @media (max-width: 743px) { - margin-bottom: 0.8rem; - } -` -interface InputProps { - $isError?: boolean -} -const Input = styled.input` - width: 100%; - padding: 1.5rem 2.4rem; - - border-radius: 1.2rem; - ${(props) => textStyle(16, 400)(props)} - color: ${theme.colors.SecondaryGray[800]}; - background-color: ${theme.colors.SecondaryGray[100]}; - - border: 1px solid - ${({ $isError }) => - $isError ? theme.colors.error : theme.colors.PrimaryBlue[100]}; - &:hover { - border: 1px solid - ${({ $isError }) => - $isError ? theme.colors.error : theme.colors.PrimaryBlue[100]}; - } -` -const InputWrapper = styled.label` - width: 100%; - height: 9.8rem; - margin-bottom: 2.4rem; - ${(props) => textStyle(18, 700)(props)} - color: ${theme.colors.SecondaryGray[800]}; - @media (max-width: 743px) { - ${(props) => textStyle(14, 700)(props)} - height: 8.8rem; - } -` - -const IconWrapper = styled.div` - width: 24px; - height: 24px; - position: relative; - left: 594px; - top: -41px; - cursor: pointer; - @media (max-width: 743px) { - left: 302px; - } -` -const ErrorPosition = styled.div` - position: relative; -` -const ErrorMessage = styled.div` - color: ${theme.colors.error}; - ${(props) => textStyle(14, 600)(props)} - margin: 0.5rem; - top: -28px; - position: absolute; -` -interface LoginFieldtProps { - label: string - type: string - id: string - placeholder: string - icon?: string | null - onIconClick?: () => void - validate?: (value: string) => string - value: string - onChange?: (e: React.ChangeEvent) => void - error?: string -} -const LoginField = ({ - // 부모에게 받음 - label, - type = 'text', - id, - placeholder, - icon = null, - onIconClick, - validate, - value, - onChange, -}: LoginFieldtProps) => { - const [error, setError] = useState('') - - const handleBlur = () => { - // 포커스를 잃었을 때 사용 - if (validate) { - setError(validate(value)) - } - } - - return ( - <> - - {label} - <> - - - {icon ? ( - {`${label} - ) : ( -
- )} - - - - {error && {error}} - - - - ) -} - -export default LoginField diff --git a/src/app/additem/page.tsx b/src/app/additem/page.tsx index 71412973..1b6055d3 100644 --- a/src/app/additem/page.tsx +++ b/src/app/additem/page.tsx @@ -1,15 +1,15 @@ 'use client' import React, { useState } from 'react' -import ItemsNavVar from '../common/ItemsNavVar' -import Button from '../common/Button' -import ButtonImage from '../common/ButtonImage' -import TextInputPlaceholder from '../common/TextInputPlaceholder' -import Tag from '../common/Tag' +import ItemsNavVar from '../../components/domain/Nav/ItemsNavVar' +import Button from '../../components/common/Button' +import ButtonImage from '../../components/common/ButtonImageInput' +import TextInputPlaceholder from '../../components/common/TextInputPlaceholder' +import Tag from '../../components/common/Tag' import styled from 'styled-components' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' +import { theme } from '../../styles/theme' +import { textStyle } from '../../styles/textStyle' const AddItem = () => { const [productName, setProductName] = useState('') @@ -117,7 +117,7 @@ const Bone = styled.div` width: 120rem; display: flex; align-items: center; - margin: 1.5rem auto auto auto; + margin: 1.5rem auto 6.9rem auto; flex-direction: column; @media (max-width: 1023px) { width: 69.6rem; diff --git a/src/app/boards/BestBoards.tsx b/src/app/boards/BestBoards.tsx index 9c978c4b..f01e2f60 100644 --- a/src/app/boards/BestBoards.tsx +++ b/src/app/boards/BestBoards.tsx @@ -1,27 +1,45 @@ -import React from 'react' +import React, { useState, useEffect } from 'react' import Image from 'next/image' -import { GetArticleType } from '../types/article' +import { useGetBestArticles } from '../../hooks/useGetBeatArticle' -import HeartInactive from '../../../public/assets/image/HeartInactive.png' -import BestBadge from '../../../public/assets/image/BestBadge.png' -import { formatDate } from '../utils/datetime' +import HeartInactive from '../../../public/assets/image/heart_inactive.png' +import BestBadge from '../../../public/assets/image/best_badge.png' +import { formatDate } from '../../utils/datetime' import styled from 'styled-components' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' +import { theme } from '../../styles/theme' +import { textStyle } from '../../styles/textStyle' -type BestBoardsProps = { - articleList?: GetArticleType -} +const BestBoards = () => { + const [bestPageSize, setBestPageSize] = useState(3) + useEffect(() => { + const handleResize = () => { + if (window.innerWidth <= 743) { + setBestPageSize(1) + } else if (window.innerWidth <= 1023) { + setBestPageSize(2) + } else { + setBestPageSize(3) + } + } + + handleResize() // 처음 렌더링 시에도 계산 + window.addEventListener('resize', handleResize) + return () => window.removeEventListener('resize', handleResize) + }, []) -const BestBoards = ({ articleList }: BestBoardsProps) => { - if (!articleList) return null + // useGetBestArticles 훅을 사용하여 베스트 게시글 목록을 가져옵니다. + const bestArticleList = useGetBestArticles(bestPageSize) + + if (!bestArticleList) { + return
게시글을 불러오는 중입니다...
+ } return ( <> 베스트 게시글 - {articleList.list.map((article) => ( + {bestArticleList.list.map((article) => ( 베스트 게시글 오피셜 아이콘 diff --git a/src/app/boards/BoardDetailArrary.tsx b/src/app/boards/BoardDetailArrary.tsx index 41041500..199be8a5 100644 --- a/src/app/boards/BoardDetailArrary.tsx +++ b/src/app/boards/BoardDetailArrary.tsx @@ -2,15 +2,15 @@ import React from 'react' -import { GetArticleIdType } from '../types/article' -import { diffDate } from '../utils/datetime' -import { formatDate } from '../utils/datetime' +import { GetArticleIdType } from '../../types/article' +import { diffDate } from '../../utils/datetime' +import { formatDate } from '../../utils/datetime' -import HeartInactive from '../../../public/assets/image/HeartInactive.png' +import HeartInactive from '../../../public/assets/image/heart_inactive.png' import styled from 'styled-components' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' +import { theme } from '../../styles/theme' +import { textStyle } from '../../styles/textStyle' import Image from 'next/image' type BestBoardsProps = { @@ -23,13 +23,15 @@ const BoardDetailArrary = ({ article }: BestBoardsProps) => { {article.content} - 게시글 상세 이미지 + {article.image && ( + 게시글 상세 이미지 + )} {/*이미지가 없을 경우 기본 이미지 적용*/} - 유저프로필사진 void - loadMore: () => void - hasMore: boolean - searchTerm: string - setSearchTerm: (value: string) => void -} -const BoardList = ({ - articleList, - selectedOption, - setSelectedOption, - loadMore, - hasMore, - searchTerm, - setSearchTerm, -}: BestBoardsProps) => { - console.log(articleList) + +const BoardList = () => { const selectList: SelectOption[] = [ { value: 'recent', name: '최신순' }, { value: 'like', name: '좋아요순' }, ] const observerRef = useRef(null) const scrollContainerRef = useRef(null) - const loadingRef = useRef(false) + const [page, setPage] = useState(1) + const [selectedOption, setSelectedOption] = useState('recent') + const [searchTerm, setSearchTerm] = useState('') + const debouncedSearchTerm = useDebounce(searchTerm, 500) + const orderBy = selectedOption === 'recent' ? 'recent' : 'like' + // 페이지네이션을 위한 상태 useEffect(() => { - if (!hasMore || !observerRef.current || !scrollContainerRef.current) return - - let observer: IntersectionObserver | null = null - - const callback = async (entries: IntersectionObserverEntry[]) => { - if (entries[0].isIntersecting && !loadingRef.current) { - loadingRef.current = true - await loadMore() - setTimeout(() => { - loadingRef.current = false - }, 500) // 시간을 넣어 무한 스크롤이 한 번에 여러번 호출되지 않게 - } + if (page !== 1) { + setPage(1) + setHasMore(true) } + }, [selectedOption, debouncedSearchTerm]) - observer = new IntersectionObserver(callback, { - root: scrollContainerRef.current, - threshold: 0.9, - }) - - observer.observe(observerRef.current) - - return () => { - if (observer) observer.disconnect() - } - }, [hasMore, loadMore]) + // 게시글 목록을 가져오는 커스텀 훅 + const { articleList, hasMore, setHasMore } = useGetArticles( + page, + orderBy, + debouncedSearchTerm + ) + // 페이지네이션을 위한 상태 + const loadMore = async () => { + if (hasMore) setPage((prev) => prev + 1) + } + //무한스크롤 + useInfiniteScroll({ + hasMore, + loadMore, + observerRef, + scrollContainerRef, + }) + + if (!articleList) { + // 데이터가 없을 때 로딩 상태를 보여줄 수 있고, 데이터가 도착하면 다시 렌더링되어 실제 게시글 목록이 보임임 + return null + } return ( <> diff --git a/src/app/boards/page.tsx b/src/app/boards/page.tsx index 78b23672..90c3bd51 100644 --- a/src/app/boards/page.tsx +++ b/src/app/boards/page.tsx @@ -1,126 +1,19 @@ 'use client' -import React, { useEffect, useState } from 'react' +import React from 'react' -import ItemsNavVar from '../common/ItemsNavVar' +import ItemsNavVar from '../../components/domain/Nav/ItemsNavVar' import BestBoards from './BestBoards' import BoardList from './BoardList' -import articleService from '../api/services/articleService' -import { GetArticleType } from '../types/article' import styled from 'styled-components' const Boards = () => { - const [articleList, setArticleList] = useState() - const [bestArticleList, setBestArticleList] = useState() - const [page, setPage] = useState(1) - const [hasMore, setHasMore] = useState(true) - const [selectedOption, setSelectedOption] = useState('recent') - const [searchTerm, setSearchTerm] = useState('') - const [debouncedSearchTerm, setDebouncedSearchTerm] = useState('') - const [bestPageSize, setBestPageSize] = useState(3) // 심화 페이지 사이즈 조절 - - const orderBy = selectedOption === 'recent' ? 'recent' : 'like' - // BestBoards에 관한 useEffect - useEffect(() => { - const timer = setTimeout(() => { - setDebouncedSearchTerm(searchTerm) - }, 500) // <- 디바운스 지연 시간 - - return () => clearTimeout(timer) - }, [searchTerm]) - - //BestBoards - useEffect(() => { - articleService - .getArticle(1, bestPageSize, 'like', '') - .then((response) => { - const sorted = [...(response.data.list || [])].sort( - (a, b) => b.likeCount - a.likeCount - ) - setBestArticleList({ - totalCount: response.data.totalCount, - list: sorted, - }) - }) - .catch((error) => { - console.error('베스트 게시글 불러오기 실패:', error) - }) - }, [bestPageSize]) - - // BoardList - useEffect(() => { - const fetchArticles = async () => { - try { - const response = await articleService.getArticle( - page, - 5, - orderBy, - debouncedSearchTerm // ← 디바운스된 검색어 사용 - ) - const newList = response.data.list || [] - - setArticleList((prev) => { - const combinedList = - page === 1 ? newList : [...(prev?.list || []), ...newList] - return { - totalCount: response.data.totalCount, - list: combinedList, - } - }) - - if (newList.length < 5) { - setHasMore(false) - } - } catch (error) { - console.error('게시글 불러오기 실패:', error) - } - } - - fetchArticles() - }, [page, selectedOption, debouncedSearchTerm]) - - // 선택/검색이 바뀌면 초기화 - useEffect(() => { - setPage(1) - setHasMore(true) - }, [selectedOption]) - - useEffect(() => { - setPage(1) - setHasMore(true) - }, [debouncedSearchTerm]) - - useEffect(() => { - const handleResize = () => { - if (window.innerWidth <= 743) { - setBestPageSize(1) - } else if (window.innerWidth <= 1023) { - setBestPageSize(2) - } else { - setBestPageSize(3) - } - } - - handleResize() // 처음 렌더링 시에도 계산 - window.addEventListener('resize', handleResize) - return () => window.removeEventListener('resize', handleResize) - }, []) return ( <> - {articleList && } - {articleList && ( - setPage((prev) => prev + 1)} - hasMore={hasMore} - searchTerm={searchTerm} - setSearchTerm={setSearchTerm} - /> - )} + + ) diff --git a/src/app/globals.css b/src/app/globals.css index ebf1acbc..2b12cd88 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -16,7 +16,6 @@ html { body { font-size: 1.6rem !important; max-width: 100vw; - overflow-x: hidden; } body { diff --git a/src/app/hooks/useCommentService.tsx b/src/app/hooks/useCommentService.tsx index 7c353758..fa369f6b 100644 --- a/src/app/hooks/useCommentService.tsx +++ b/src/app/hooks/useCommentService.tsx @@ -1,9 +1,9 @@ 'use client' import { useEffect, useState } from 'react' -import { GetCommentType } from '../types/comment' +import { GetCommentType } from '../../types/comment' -import commentService from '../../../src/app/api/services/commentService' +import commentService from '../../lib/api/service/commentService' export const useGetCommentService = (productId: number, limit = 3) => { const [productQuestion, setProductQuestion] = useState({ diff --git a/src/app/hooks/useProductService.tsx b/src/app/hooks/useProductService.tsx index b13083b0..48afc7c1 100644 --- a/src/app/hooks/useProductService.tsx +++ b/src/app/hooks/useProductService.tsx @@ -2,8 +2,8 @@ import { useEffect, useState } from 'react' -import productService from '../../../src/app/api/services/productService' -import { GetProductIdTypes } from '../types/product' +import productService from '../../lib/api/service/productService' +import { GetProductIdTypes } from '../../types/product' export const useGetProductId = (productId: number) => { const [productsId, setProductsId] = useState() diff --git a/src/app/login/login.module.scss b/src/app/login/login.module.scss new file mode 100644 index 00000000..c9b99113 --- /dev/null +++ b/src/app/login/login.module.scss @@ -0,0 +1,109 @@ +@import '../../styles/variables.scss'; + +.bone { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + width: 64rem; + margin: 23.1rem auto 23rem auto; + @media (max-width: 1023px) { + margin: 19rem auto 32rem auto; + } + @media (max-width: 743px) { + margin: 8rem auto 23rem auto; + width: 34.3rem; + } +} +.logo-container { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 4rem; + @media (max-width: 743px) { + width: 19.8rem; + height: 6.6rem; + margin-bottom: 2.4rem; + } +} +.logo-panda { + img { + width: 10.3rem; + height: 10.3rem; + margin-right: 2.2rem; + display: flex; + } + @media (max-width: 743px) { + img { + width: 5.1rem; + height: 5.1rem; + margin-right: 1.1rem; + display: flex; + } + } +} +.logo-panda-text { + img { + width: 26.6rem; + height: 9rem; + display: flex; + align-items: center; + } + @media (max-width: 743px) { + img { + width: 13.3rem; + height: 4.5rem; + } + } +} + +.button-wrapper { + margin-bottom: 2.4rem; + width: 100%; +} +.login-button { + padding: 1.2rem 29.4rem; + width: max-content; + @media (max-width: 743px) { + padding: 1.2rem 14.5rem; + } +} +.simple-login-wrapper { + height: fit-content; + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + background-color: #e6f2ff; + padding: 1.6rem 2.3rem; + margin-bottom: 2.4rem; +} +.simple-login { + @include apply-font($font-16, 500); + color: $secondaryGray-800; +} +.image-wrapper { + height: 4.2rem; + width: 10rem; + display: flex; + gap: 1.6rem; + flex-direction: row; + align-items: center; + img { + width: 4.2rem; + height: 4.2rem; + display: flex; + } +} +.footer-container { + display: flex; + justify-content: space-between; +} +.first { + @include apply-font($font-14, 500); + color: $secondaryGray-800; +} +.register { + text-decoration: underline; + color: $primaryBlue-100; +} diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 074190e9..531f752b 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -3,129 +3,19 @@ import React, { useState, useEffect } from 'react' import Link from 'next/link' import { useRouter } from 'next/navigation' +import Image from 'next/image' -import LoginField from '../LoginAndSignup/LoginField' -import Button from '../common/Button' +import LoginField from '../../components/domain/LoginAndSignup/LoginField' +import Button from '../../components/common/Button' -import Logo from '../../../public/assets/image/Logo.png' -import LogoFace from '../../../public/assets/image/LogoFace.png' -import Google from '../../../public/assets/svg/Google.svg' -import Kakao from '../../../public/assets/svg/Kakao.svg' +import Logo from '../../../public/assets/image/logo_text.png' +import LogoFace from '../../../public/assets/image/logo_face.png' +import Google from '../../../public/assets/svg/google.svg' +import Kakao from '../../../public/assets/svg/kakao.svg' import VisibillityOff from '../../../public/assets/svg/btn_visibillity_off.svg' import Visibillity from '../../../public/assets/svg/btn_visibillity.svg' -import styled from 'styled-components' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' -import Image from 'next/image' - -const Bone = styled.div` - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - width: 64rem; - margin: 23.1rem auto auto auto; - @media (max-width: 1023px) { - margin: 19rem auto auto auto; - } - @media (max-width: 743px) { - margin: 8rem auto auto auto; - width: 34.3rem; - } -` -const LogoContainer = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 4rem; - @media (max-width: 743px) { - width: 19.8rem; - height: 6.6rem; - margin-bottom: 2.4rem; - } -` -const LogoPanda = styled.div` - img { - width: 10.3rem; - height: 10.3rem; - margin-right: 2.2rem; - display: flex; - } - @media (max-width: 743px) { - img { - width: 5.1rem; - height: 5.1rem; - margin-right: 1.1rem; - display: flex; - } - } -` -const LogoPandaText = styled.div` - img { - width: 26.6rem; - height: 9rem; - display: flex; - align-items: center; - } - @media (max-width: 743px) { - img { - width: 13.3rem; - height: 4.5rem; - } - } -` - -const ButtonWrapper = styled.div` - margin-bottom: 2.4rem; - width: 100%; -` -const LoginButton = styled(Button)` - padding: 1.2rem 29.4rem; - width: max-content; - @media (max-width: 743px) { - padding: 1.2rem 14.5rem; - } -` -const SimpleLoginWrapper = styled.div` - height: fit-content; - width: 100%; - display: flex; - align-items: center; - justify-content: space-between; - background-color: #e6f2ff; - padding: 1.6rem 2.3rem; - margin-bottom: 2.4rem; -` -const SimpleLogin = styled.div` - ${(props) => textStyle(16, 500)(props)} - color: ${theme.colors.SecondaryGray[800]}; -` -const ImageWrapper = styled.div` - height: 4.2rem; - width: 10rem; - display: flex; - gap: 1.6rem; - flex-direction: row; - align-items: center; - img { - width: 4.2rem; - height: 4.2rem; - display: flex; - } -` -const FooterContainer = styled.div` - display: flex; - justify-content: space-between; -` -const First = styled.div` - ${(props) => textStyle(14, 500)(props)} - color: ${theme.colors.SecondaryGray[800]}; -` -const Register = styled.div` - text-decoration: underline; - color: ${theme.colors.PrimaryBlue[100]}; -` +import styles from './login.module.scss' const Login = () => { const [email, setEmail] = useState('') @@ -135,6 +25,7 @@ const Login = () => { const [showPassword, setShowPassword] = useState(false) const [isState, setIsState] = useState(false) const router = useRouter() + const togglePasswordVisibility = () => { setShowPassword((prev) => !prev) } @@ -172,19 +63,19 @@ const Login = () => { setIsState(valid) }, [email, password]) return ( - - - +
+
+
판다마켓 로고 사진 - - +
+
판다마켓 로고 사진 - - +
+
{ placeholder="비밀번호를 입력해주세요" id="password" icon={showPassword ? Visibillity : VisibillityOff} - onIconClick={togglePasswordVisibility} + onIconClick={(e) => { + e.stopPropagation() + togglePasswordVisibility() + }} validate={validatePassword} value={password} onChange={(e) => { @@ -211,31 +105,36 @@ const Login = () => { }} error={passwordError} /> - - +
+ +
+
+
간편 로그인하기
+
구글 로고 사진 카카오 로고 사진 - - - - 판다마켓이 처음이신가요?   - +
+
+
+
판다마켓이 처음이신가요?  
+
회원가입 - - - +
+
+
) } diff --git a/src/app/page.style.ts b/src/app/page.style.ts index 1f2f390f..17719a23 100644 --- a/src/app/page.style.ts +++ b/src/app/page.style.ts @@ -1,12 +1,12 @@ import styled from 'styled-components' -import { theme } from './styles/theme' -import { textStyle } from './styles/textStyle' -import Button from './common/Button' +import { theme } from '../styles/theme' +import { textStyle } from '../styles/textStyle' +import Button from '../components/common/Button' export const HeaderTop = styled.header` display: flex; justify-content: center; - position: sticky; + top: 0; background-color: #ffffff; ` @@ -90,6 +90,7 @@ export const HeaderLogin = styled.a` export const HeaderMain = styled.div` width: 100%; background-color: #cfe5ff; + position: relative; ` export const HeaderMainContainer = styled.div` diff --git a/src/app/page.tsx b/src/app/page.tsx index 31a0c09b..cd10a712 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -4,10 +4,10 @@ import Image from 'next/image' import Link from 'next/link' import * as S from './page.style' -import Button from './common/Button' +import Button from '../components/common/Button' -import Logo from '../../public/assets/image/Logo.png' -import LogoFace from '../../public/assets/image/LogoFace.png' +import Logo from '../../public/assets/image/logo_text.png' +import LogoFace from '../../public/assets/image/logo_face.png' import HomeTop from '../../public/assets/image/home_top.png' import HomeBottom from '../../public/assets/image/home_bottom.png' import HomeHotItems from '../../public/assets/image/home_hot_items.png' diff --git "a/src/app/Privacy/Privacy\342\200\231.jsx" b/src/app/privacy/page.tsx similarity index 100% rename from "src/app/Privacy/Privacy\342\200\231.jsx" rename to src/app/privacy/page.tsx diff --git a/src/app/signup/page.tsx b/src/app/signup/page.tsx index 6beddfb3..ca3de8fa 100644 --- a/src/app/signup/page.tsx +++ b/src/app/signup/page.tsx @@ -3,128 +3,19 @@ import React, { useState, useEffect } from 'react' import Image from 'next/image' import { useRouter } from 'next/navigation' +import Link from 'next/link' -import LoginField from '../LoginAndSignup/LoginField' -import Button from '../common/Button' +import LoginField from '../../components/domain/LoginAndSignup/LoginField' +import Button from '../../components/common/Button' -import Logo from '../../../public/assets/image/Logo.png' -import LogoFace from '../../../public/assets/image/LogoFace.png' -import Google from '../../../public/assets/svg/Google.svg' -import Kakao from '../../../public/assets/svg/Kakao.svg' +import Logo from '../../../public/assets/image/logo_text.png' +import LogoFace from '../../../public/assets/image/logo_face.png' +import Google from '../../../public/assets/svg/google.svg' +import Kakao from '../../../public/assets/svg/kakao.svg' import VisibillityOff from '../../../public/assets/svg/btn_visibillity_off.svg' import Visibillity from '../../../public/assets/svg/btn_visibillity.svg' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' -import styled from 'styled-components' -import Link from 'next/link' - -const Bone = styled.div` - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - width: 64rem; - margin: 23.1rem auto auto auto; - @media (max-width: 1023px) { - margin: 19rem auto auto auto; - } - @media (max-width: 743px) { - margin: 8rem auto auto auto; - width: 34.3rem; - } -` -const LogoContainer = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 4rem; - @media (max-width: 743px) { - width: 19.8rem; - height: 6.6rem; - margin-bottom: 2.4rem; - } -` -const LogoPanda = styled.div` - img { - width: 10.3rem; - height: 10.3rem; - margin-right: 2.2rem; - display: flex; - } - @media (max-width: 743px) { - img { - width: 5.1rem; - height: 5.1rem; - margin-right: 1.1rem; - display: flex; - } - } -` -const LogoPandaText = styled.div` - img { - width: 26.6rem; - height: 9rem; - display: flex; - align-items: center; - } - @media (max-width: 743px) { - img { - width: 13.3rem; - height: 4.5rem; - } - } -` -const ButtonWrapper = styled.div` - margin-bottom: 2.4rem; - width: 100%; -` -const SignupButton = styled(Button)` - padding: 1.2rem 28.5rem; - width: max-content; - @media (max-width: 743px) { - padding: 1.2rem 13.6rem; - } -` -const SimpleLoginWrapper = styled.div` - height: fit-content; - width: 100%; - display: flex; - align-items: center; - justify-content: space-between; - background-color: #e6f2ff; - padding: 1.6rem 2.3rem; - margin-bottom: 2.4rem; -` -const SimpleLogin = styled.div` - ${(props) => textStyle(16, 500)(props)} - color: ${theme.colors.SecondaryGray[800]}; -` -const ImageWrapper = styled.div` - height: 4.2rem; - width: 10rem; - display: flex; - gap: 1.6rem; - flex-direction: row; - align-items: center; - img { - width: 4.2rem; - height: 4.2rem; - display: flex; - } -` -const FooterContainer = styled.div` - display: flex; - justify-content: space-between; -` -const First = styled.div` - ${(props) => textStyle(14, 500)(props)} - color: ${theme.colors.SecondaryGray[800]}; -` -const Register = styled.div` - text-decoration: underline; - color: ${theme.colors.PrimaryBlue[100]}; -` +import styles from './signup.module.scss' const Signup = () => { const [email, setEmail] = useState('') @@ -187,19 +78,19 @@ const Signup = () => { setIsState(valid) }, [email, password, passwordConfirm]) return ( - - - +
+
+
판다마켓 로고 사진 - - +
+
판다마켓 로고 사진 - - +
+
{ }} error={passwordConfirmError} /> - - +
+ +
+
+
간편 로그인하기
+
구글 로고 사진 카카오 로고 사진 - - - - 이미 회원이신가요?   - +
+
+
+
이미 회원이신가요?  
+
로그인 - - - +
+
+
) } export default Signup + +// const Bone = styled.div` +// display: flex; +// align-items: center; +// justify-content: center; +// flex-direction: column; +// width: 64rem; +// margin: 23.1rem auto auto auto; +// @media (max-width: 1023px) { +// margin: 19rem auto auto auto; +// } +// @media (max-width: 743px) { +// margin: 8rem auto auto auto; +// width: 34.3rem; +// } +// ` +// const LogoContainer = styled.div` +// display: flex; +// align-items: center; +// justify-content: space-between; +// margin-bottom: 4rem; +// @media (max-width: 743px) { +// width: 19.8rem; +// height: 6.6rem; +// margin-bottom: 2.4rem; +// } +// ` +// const LogoPanda = styled.div` +// img { +// width: 10.3rem; +// height: 10.3rem; +// margin-right: 2.2rem; +// display: flex; +// } +// @media (max-width: 743px) { +// img { +// width: 5.1rem; +// height: 5.1rem; +// margin-right: 1.1rem; +// display: flex; +// } +// } +// ` +// const LogoPandaText = styled.div` +// img { +// width: 26.6rem; +// height: 9rem; +// display: flex; +// align-items: center; +// } +// @media (max-width: 743px) { +// img { +// width: 13.3rem; +// height: 4.5rem; +// } +// } +// ` +// const ButtonWrapper = styled.div` +// margin-bottom: 2.4rem; +// width: 100%; +// ` +// const SignupButton = styled(Button)` +// padding: 1.2rem 28.5rem; +// width: max-content; +// @media (max-width: 743px) { +// padding: 1.2rem 13.6rem; +// } +// ` +// const SimpleLoginWrapper = styled.div` +// height: fit-content; +// width: 100%; +// display: flex; +// align-items: center; +// justify-content: space-between; +// background-color: #e6f2ff; +// padding: 1.6rem 2.3rem; +// margin-bottom: 2.4rem; +// ` +// const SimpleLogin = styled.div` +// ${(props) => textStyle(16, 500)(props)} +// color: ${theme.colors.SecondaryGray[800]}; +// ` +// const ImageWrapper = styled.div` +// height: 4.2rem; +// width: 10rem; +// display: flex; +// gap: 1.6rem; +// flex-direction: row; +// align-items: center; +// img { +// width: 4.2rem; +// height: 4.2rem; +// display: flex; +// } +// ` +// const FooterContainer = styled.div` +// display: flex; +// justify-content: space-between; +// ` +// const First = styled.div` +// ${(props) => textStyle(14, 500)(props)} +// color: ${theme.colors.SecondaryGray[800]}; +// ` +// const Register = styled.div` +// text-decoration: underline; +// color: ${theme.colors.PrimaryBlue[100]}; +// ` diff --git a/src/app/signup/signup.module.scss b/src/app/signup/signup.module.scss new file mode 100644 index 00000000..8ad3a43b --- /dev/null +++ b/src/app/signup/signup.module.scss @@ -0,0 +1,108 @@ +@import '../../styles/variables.scss'; + +.bone { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + width: 64rem; + margin: 23.1rem auto 17rem auto; + @media (max-width: 1023px) { + margin: 19rem auto 23rem auto; + } + @media (max-width: 743px) { + margin: 8rem auto 17rem auto; + width: 34.3rem; + } +} +.logo-container { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 4rem; + @media (max-width: 743px) { + width: 19.8rem; + height: 6.6rem; + margin-bottom: 2.4rem; + } +} +.logo-panda { + img { + width: 10.3rem; + height: 10.3rem; + margin-right: 2.2rem; + display: flex; + } + @media (max-width: 743px) { + img { + width: 5.1rem; + height: 5.1rem; + margin-right: 1.1rem; + display: flex; + } + } +} +.logo-panda-text { + img { + width: 26.6rem; + height: 9rem; + display: flex; + align-items: center; + } + @media (max-width: 743px) { + img { + width: 13.3rem; + height: 4.5rem; + } + } +} +.button-wrapper { + margin-bottom: 2.4rem; + width: 100%; +} +.signup-button { + padding: 1.2rem 28.5rem; + width: max-content; + @media (max-width: 743px) { + padding: 1.2rem 13.6rem; + } +} +.simple-login-wrapper { + height: fit-content; + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + background-color: #e6f2ff; + padding: 1.6rem 2.3rem; + margin-bottom: 2.4rem; +} +.simple-login { + @include apply-font($font-16, 500); + color: $secondaryGray-800; +} +.image-wrapper { + height: 4.2rem; + width: 10rem; + display: flex; + gap: 1.6rem; + flex-direction: row; + align-items: center; + img { + width: 4.2rem; + height: 4.2rem; + display: flex; + } +} +.footer-container { + display: flex; + justify-content: space-between; +} +.first { + @include apply-font($font-14, 500); + color: $secondaryGray-800; +} +.register { + text-decoration: underline; + color: $primaryBlue-100; +} diff --git a/src/app/styles/Home.module.css b/src/app/styles/Home.module.css deleted file mode 100644 index 6676d2c6..00000000 --- a/src/app/styles/Home.module.css +++ /dev/null @@ -1,229 +0,0 @@ -.main { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - padding: 6rem; - min-height: 100vh; -} - -.description { - display: inherit; - justify-content: inherit; - align-items: inherit; - font-size: 0.85rem; - max-width: var(--max-width); - width: 100%; - z-index: 2; - font-family: var(--font-mono); -} - -.description a { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; -} - -.description p { - position: relative; - margin: 0; - padding: 1rem; - background-color: rgba(var(--callout-rgb), 0.5); - border: 1px solid rgba(var(--callout-border-rgb), 0.3); - border-radius: var(--border-radius); -} - -.code { - font-weight: 700; - font-family: var(--font-mono); -} - -.grid { - display: grid; - grid-template-columns: repeat(4, minmax(25%, auto)); - max-width: 100%; - width: var(--max-width); -} - -.card { - padding: 1rem 1.2rem; - border-radius: var(--border-radius); - background: rgba(var(--card-rgb), 0); - border: 1px solid rgba(var(--card-border-rgb), 0); - transition: background 200ms, border 200ms; -} - -.card span { - display: inline-block; - transition: transform 200ms; -} - -.card h2 { - font-weight: 600; - margin-bottom: 0.7rem; -} - -.card p { - margin: 0; - opacity: 0.6; - font-size: 0.9rem; - line-height: 1.5; - max-width: 30ch; -} - -.center { - display: flex; - justify-content: center; - align-items: center; - position: relative; - padding: 4rem 0; -} - -.center::before { - background: var(--secondary-glow); - border-radius: 50%; - width: 480px; - height: 360px; - margin-left: -400px; -} - -.center::after { - background: var(--primary-glow); - width: 240px; - height: 180px; - z-index: -1; -} - -.center::before, -.center::after { - content: ''; - left: 50%; - position: absolute; - filter: blur(45px); - transform: translateZ(0); -} - -.logo { - position: relative; -} -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - .card:hover { - background: rgba(var(--card-rgb), 0.1); - border: 1px solid rgba(var(--card-border-rgb), 0.15); - } - - .card:hover span { - transform: translateX(4px); - } -} - -@media (prefers-reduced-motion) { - .card:hover span { - transform: none; - } -} - -/* Mobile */ -@media (max-width: 700px) { - .content { - padding: 4rem; - } - - .grid { - grid-template-columns: 1fr; - margin-bottom: 120px; - max-width: 320px; - text-align: center; - } - - .card { - padding: 1rem 2.5rem; - } - - .card h2 { - margin-bottom: 0.5rem; - } - - .center { - padding: 8rem 0 6rem; - } - - .center::before { - transform: none; - height: 300px; - } - - .description { - font-size: 0.8rem; - } - - .description a { - padding: 1rem; - } - - .description p, - .description div { - display: flex; - justify-content: center; - position: fixed; - width: 100%; - } - - .description p { - align-items: center; - inset: 0 0 auto; - padding: 2rem 1rem 1.4rem; - border-radius: 0; - border: none; - border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); - background: linear-gradient( - to bottom, - rgba(var(--background-start-rgb), 1), - rgba(var(--callout-rgb), 0.5) - ); - background-clip: padding-box; - backdrop-filter: blur(24px); - } - - .description div { - align-items: flex-end; - pointer-events: none; - inset: auto 0 0; - padding: 2rem; - height: 200px; - background: linear-gradient( - to bottom, - transparent 0%, - rgb(var(--background-end-rgb)) 40% - ); - z-index: 1; - } -} - -/* Tablet and Smaller Desktop */ -@media (min-width: 701px) and (max-width: 1120px) { - .grid { - grid-template-columns: repeat(2, 50%); - } -} - -@media (prefers-color-scheme: dark) { - .vercelLogo { - filter: invert(1); - } - - .logo { - filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); - } -} - -@keyframes rotate { - from { - transform: rotate(360deg); - } - to { - transform: rotate(0deg); - } -} diff --git a/src/app/common/Button.tsx b/src/components/common/Button.tsx similarity index 100% rename from src/app/common/Button.tsx rename to src/components/common/Button.tsx diff --git a/src/app/common/ButtonImage.tsx b/src/components/common/ButtonImageInput.tsx similarity index 93% rename from src/app/common/ButtonImage.tsx rename to src/components/common/ButtonImageInput.tsx index 8aa315dd..c76dbc45 100644 --- a/src/app/common/ButtonImage.tsx +++ b/src/components/common/ButtonImageInput.tsx @@ -1,11 +1,11 @@ import React, { useRef, useState } from 'react' -import Plus from '../../../public/assets/svg/Plus.svg' -import Delete from '../../../public/assets/svg/Delete.svg' +import Plus from '../../../public/assets/svg/plus_icon.svg' +import Delete from '../../../public/assets/svg/delete_tag.svg' import styled from 'styled-components' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' +import { theme } from '../../styles/theme' +import { textStyle } from '../../styles/textStyle' import Image from 'next/image' const ButtonImage = () => { @@ -16,7 +16,7 @@ const ButtonImage = () => { const handleButton = () => { if (fileInputRef.current) { // if문을 쓰는 이유는 useRef 값이 null일 수도 있기 때문문 - fileInputRef.current.click() + fileInputRef.current?.click() } } diff --git a/src/app/common/DropDown.tsx b/src/components/common/DropDown.tsx similarity index 93% rename from src/app/common/DropDown.tsx rename to src/components/common/DropDown.tsx index ea10e2d0..6daf21c3 100644 --- a/src/app/common/DropDown.tsx +++ b/src/components/common/DropDown.tsx @@ -3,11 +3,11 @@ import React, { useRef, useState, useEffect } from 'react' import styled from 'styled-components' import Image from 'next/image' -import ArrowDown from '../../../public/assets/image/ArrowDown.png' -import Sort from '../../../public/assets/svg/Sort.svg' +import ArrowDown from '../../../public/assets/image/arrow_down.png' +import Sort from '../../../public/assets/svg/sort_arrow.svg' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' +import { theme } from '../../styles/theme' +import { textStyle } from '../../styles/textStyle' type OptionType = { name: string diff --git a/src/app/common/Tag.tsx b/src/components/common/Tag.tsx similarity index 85% rename from src/app/common/Tag.tsx rename to src/components/common/Tag.tsx index d39a4b04..8c2e43c5 100644 --- a/src/app/common/Tag.tsx +++ b/src/components/common/Tag.tsx @@ -1,10 +1,10 @@ import React from 'react' -import Delete from '../../../public/assets/svg/Delete.svg' +import Delete from '../../../public/assets/svg/delete_tag.svg' import styled from 'styled-components' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' +import { theme } from '../../styles/theme' +import { textStyle } from '../../styles/textStyle' import Image from 'next/image' interface TagProps { diff --git a/src/app/common/TextInputPlaceholder.tsx b/src/components/common/TextInputPlaceholder.tsx similarity index 93% rename from src/app/common/TextInputPlaceholder.tsx rename to src/components/common/TextInputPlaceholder.tsx index b1c3ffdf..8b5418b0 100644 --- a/src/app/common/TextInputPlaceholder.tsx +++ b/src/components/common/TextInputPlaceholder.tsx @@ -1,7 +1,7 @@ import React from 'react' import styled from 'styled-components' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' +import { theme } from '../../styles/theme' +import { textStyle } from '../../styles/textStyle' interface TextInputPlaceholderProps { placeholder?: string diff --git a/src/components/domain/LoginAndSignup/LoginField.module.scss b/src/components/domain/LoginAndSignup/LoginField.module.scss new file mode 100644 index 00000000..4fb57dd7 --- /dev/null +++ b/src/components/domain/LoginAndSignup/LoginField.module.scss @@ -0,0 +1,62 @@ +@import '../../../styles/variables.scss'; + +.field { + display: inline-block; + margin-bottom: 1.6rem; + @media (max-width: 743px) { + margin-bottom: 0.8rem; + } +} + +.input { + width: 100%; + padding: 1.5rem 2.4rem; + border-radius: 1.2rem; + @include apply-font($font-16, 400); + color: $secondaryGray-800; + background-color: $secondaryGray-100; + border: 1px solid $primaryBlue-100; + transition: border 0.2s; + + &:hover { + border-color: $primaryBlue-100; + } +} + +.error { + border-color: $error; +} +.input-wrapper { + width: 100%; + height: 9.8rem; + margin-bottom: 2.4rem; + @include apply-font($font-18, 700); + color: $secondaryGray-800; + + @media (max-width: 743px) { + height: 8.8rem; + @include apply-font($font-14, 700); + } +} + +.icon-wrapper { + width: 24px; + height: 24px; + position: relative; + left: 594px; + top: -41px; + cursor: pointer; + @media (max-width: 743px) { + left: 302px; + } +} +.error-position { + position: relative; +} +.error-message { + color: $error; + @include apply-font($font-14, 600); + margin: 0.5rem; + position: absolute; + top: -0.5rem; +} diff --git a/src/components/domain/LoginAndSignup/LoginField.tsx b/src/components/domain/LoginAndSignup/LoginField.tsx new file mode 100644 index 00000000..ddee0b73 --- /dev/null +++ b/src/components/domain/LoginAndSignup/LoginField.tsx @@ -0,0 +1,81 @@ +import React, { useState } from 'react' +import Image from 'next/image' + +import styles from './LoginField.module.scss' +import clsx from 'clsx' + +interface LoginFieldtProps { + label: string + type: string + id: string + placeholder: string + icon?: string | null + onIconClick?: (e: React.MouseEvent) => void + validate?: (value: string) => string + value: string + onChange?: (e: React.ChangeEvent) => void + error?: string +} +const LoginField = ({ + label, + type = 'text', + id, + placeholder, + icon = null, + onIconClick, + validate, + value, + onChange, +}: LoginFieldtProps) => { + const [error, setError] = useState('') + + const handleBlur = () => { + if (validate) { + setError(validate(value)) + } + } + + return ( +
+ {/* label 하지 않은 이유는 아이콘 클릭시 input에 포커스 가기 때문에 label을 삭제하고 div를 넣음*/} +
{label}
+
+ + {icon && ( +
{ + e.stopPropagation() + if (onIconClick) onIconClick(e) + }} + tabIndex={-1} + style={{ + position: 'absolute', + right: '16px', + top: '50%', + transform: 'translateY(-50%)', + }} + > + {`${label} +
+ )} +
+
+ {error &&
{error}
} +
+
+ ) +} + +export default LoginField diff --git a/src/app/common/ItemsNavVar.tsx b/src/components/domain/Nav/ItemsNavVar.tsx similarity index 92% rename from src/app/common/ItemsNavVar.tsx rename to src/components/domain/Nav/ItemsNavVar.tsx index a9e45c71..6a63fdef 100644 --- a/src/app/common/ItemsNavVar.tsx +++ b/src/components/domain/Nav/ItemsNavVar.tsx @@ -1,13 +1,13 @@ import Link from 'next/link' import React from 'react' -import LogoFace from '../../../public/assets/image/LogoFace.png' -import Logo from '../../../public/assets/image/Logo.png' -import ProfileIcon from '../../../public/assets/svg/ProfileIcon.svg' +import LogoFace from '../../../../public/assets/image/logo_face.png' +import Logo from '../../../../public/assets/image/logo_text.png' +import ProfileIcon from '../../../../public/assets/svg/profile_icon.svg' import styled from 'styled-components' -import { theme } from '../styles/theme' -import { textStyle } from '../styles/textStyle' +import { theme } from '../../../styles/theme' +import { textStyle } from '../../../styles/textStyle' import Image from 'next/image' interface ItemsNavVarProps { diff --git a/src/app/Nav/NavVArLayout.tsx b/src/components/domain/Nav/NavVArLayout.tsx similarity index 92% rename from src/app/Nav/NavVArLayout.tsx rename to src/components/domain/Nav/NavVArLayout.tsx index c3697b42..96a02179 100644 --- a/src/app/Nav/NavVArLayout.tsx +++ b/src/components/domain/Nav/NavVArLayout.tsx @@ -1,5 +1,5 @@ import { Outlet, useLocation } from 'react-router-dom' -import ItemsNavVar from '../common/ItemsNavVar' +import ItemsNavVar from './ItemsNavVar' const NavVArLayout = () => { const { pathname } = useLocation() // 현재 페이지가 어떤 페이지인지 감지 diff --git a/src/hooks/useDebounce.ts b/src/hooks/useDebounce.ts new file mode 100644 index 00000000..ee512d60 --- /dev/null +++ b/src/hooks/useDebounce.ts @@ -0,0 +1,10 @@ +import { useState, useEffect } from 'react' + +export function useDebounce(value: T, delay: number): T { + const [debouncedValue, setDebouncedValue] = useState(value) + useEffect(() => { + const handler = setTimeout(() => setDebouncedValue(value), delay) + return () => clearTimeout(handler) + }, [value, delay]) + return debouncedValue +} diff --git a/src/hooks/useGetArticle.ts b/src/hooks/useGetArticle.ts new file mode 100644 index 00000000..32da322a --- /dev/null +++ b/src/hooks/useGetArticle.ts @@ -0,0 +1,40 @@ +import { useEffect, useState } from 'react' +import articleService from '../lib/api/service/articleService' +import { GetArticleType } from '../types/article' + +export function useGetArticles( + page: number, + orderBy: string, + searchTerm: string +) { + const [articleList, setArticleList] = useState() + const [hasMore, setHasMore] = useState(true) + + useEffect(() => { + const fetchArticles = async () => { + try { + const response = await articleService.getArticle( + page, + 5, + orderBy, + searchTerm + ) + const newList = response.data.list || [] + setArticleList((prev) => { + const combinedList = + page === 1 ? newList : [...(prev?.list || []), ...newList] + return { + totalCount: response.data.totalCount, + list: combinedList, + } + }) + if (newList.length < 5) setHasMore(false) + } catch (error) { + console.error('게시글 불러오기 실패:', error) + } + } + fetchArticles() + return () => {} + }, [page, orderBy, searchTerm]) + return { articleList, setArticleList, hasMore, setHasMore } +} diff --git a/src/hooks/useGetBeatArticle.ts b/src/hooks/useGetBeatArticle.ts new file mode 100644 index 00000000..16491fe6 --- /dev/null +++ b/src/hooks/useGetBeatArticle.ts @@ -0,0 +1,24 @@ +import { useEffect, useState } from 'react' +import articleService from '../lib/api/service/articleService' +import { GetArticleType } from '../types/article' + +export function useGetBestArticles(pageSize: number) { + const [bestArticleList, setBestArticleList] = useState() + useEffect(() => { + articleService + .getArticle(1, pageSize, 'like', '') + .then((response) => { + const sorted = [...(response.data.list || [])].sort( + (a, b) => b.likeCount - a.likeCount + ) + setBestArticleList({ + totalCount: response.data.totalCount, + list: sorted, + }) + }) + .catch((error) => { + console.error('베스트 게시글 불러오기 실패:', error) + }) + }, [pageSize]) + return bestArticleList +} diff --git a/src/hooks/useInfiniteScroll.ts b/src/hooks/useInfiniteScroll.ts new file mode 100644 index 00000000..78d465a3 --- /dev/null +++ b/src/hooks/useInfiniteScroll.ts @@ -0,0 +1,40 @@ +import { useEffect, RefObject } from 'react' + +type UseInfiniteScrollProps = { + hasMore: boolean + loadMore: () => void + observerRef: RefObject + scrollContainerRef: RefObject +} + +export function useInfiniteScroll({ + hasMore, + loadMore, + observerRef, + scrollContainerRef, +}: UseInfiniteScrollProps) { + useEffect(() => { + if (!hasMore || !observerRef.current || !scrollContainerRef.current) return + + let loading = false + + const callback = async (entries: IntersectionObserverEntry[]) => { + if (entries[0].isIntersecting && !loading) { + loading = true + await loadMore() + setTimeout(() => { + loading = false + }, 500) + } + } + + const observer = new IntersectionObserver(callback, { + root: scrollContainerRef.current, + threshold: 0.9, + }) + + observer.observe(observerRef.current) + + return () => observer.disconnect() + }, [hasMore, loadMore, observerRef, scrollContainerRef]) +} diff --git a/src/app/api/client/interceptors.tsx b/src/lib/api/client/interceptors.tsx similarity index 100% rename from src/app/api/client/interceptors.tsx rename to src/lib/api/client/interceptors.tsx diff --git a/src/app/api/client/requestor.tsx b/src/lib/api/client/requestor.tsx similarity index 100% rename from src/app/api/client/requestor.tsx rename to src/lib/api/client/requestor.tsx diff --git a/src/app/api/services/articleService.tsx b/src/lib/api/service/articleService.tsx similarity index 94% rename from src/app/api/services/articleService.tsx rename to src/lib/api/service/articleService.tsx index 5b50cb53..a021dcc1 100644 --- a/src/app/api/services/articleService.tsx +++ b/src/lib/api/service/articleService.tsx @@ -1,6 +1,6 @@ import requestor from '../client/requestor' import { AxiosResponse } from 'axios' -import { GetArticleType, GetArticleIdType } from '@/app/types/article' +import { GetArticleType, GetArticleIdType } from '../../../types/article' class ArticleService { postArticle(body) { diff --git a/src/app/api/services/authService.jsx b/src/lib/api/service/authService.jsx similarity index 100% rename from src/app/api/services/authService.jsx rename to src/lib/api/service/authService.jsx diff --git a/src/app/api/services/commentService.tsx b/src/lib/api/service/commentService.tsx similarity index 95% rename from src/app/api/services/commentService.tsx rename to src/lib/api/service/commentService.tsx index 140aff7a..30c25fdc 100644 --- a/src/app/api/services/commentService.tsx +++ b/src/lib/api/service/commentService.tsx @@ -1,7 +1,7 @@ import requestor from '../client/requestor' import { AxiosResponse } from 'axios' -import { PostCommentType, GetCommentType } from '../../types/comment' +import { PostCommentType, GetCommentType } from '../../../types/comment' class CommentService { // 틀릴 수도 diff --git a/src/app/api/services/imageService.jsx b/src/lib/api/service/imageService.jsx similarity index 100% rename from src/app/api/services/imageService.jsx rename to src/lib/api/service/imageService.jsx diff --git a/src/app/api/services/productService.tsx b/src/lib/api/service/productService.tsx similarity index 95% rename from src/app/api/services/productService.tsx rename to src/lib/api/service/productService.tsx index 7cc2132a..990937ae 100644 --- a/src/app/api/services/productService.tsx +++ b/src/lib/api/service/productService.tsx @@ -1,7 +1,7 @@ import requestor from '../client/requestor' import { AxiosResponse } from 'axios' -import { GetProductIdTypes } from '../../types/product' +import { GetProductIdTypes } from '../../../types/product' class ProductService { //틀릴 수도 diff --git a/src/app/api/services/userService.jsx b/src/lib/api/service/userService.jsx similarity index 100% rename from src/app/api/services/userService.jsx rename to src/lib/api/service/userService.jsx diff --git a/src/app/styles/GlobalStyles.tsx b/src/styles/GlobalStyles.tsx similarity index 95% rename from src/app/styles/GlobalStyles.tsx rename to src/styles/GlobalStyles.tsx index 1d0b9566..ac2e8edd 100644 --- a/src/app/styles/GlobalStyles.tsx +++ b/src/styles/GlobalStyles.tsx @@ -9,7 +9,6 @@ const GlobalStyles = createGlobalStyle` word-break: keep-all; } body { - overflow-y: hidden; font-family: 'Arial', sans-serif; background-color: #ffffff; color: #212529; diff --git a/src/app/styles/globals.css b/src/styles/globals.css similarity index 100% rename from src/app/styles/globals.css rename to src/styles/globals.css diff --git a/src/app/styles/reset.css b/src/styles/reset.css similarity index 100% rename from src/app/styles/reset.css rename to src/styles/reset.css diff --git a/src/app/styles/textStyle.ts b/src/styles/textStyle.ts similarity index 100% rename from src/app/styles/textStyle.ts rename to src/styles/textStyle.ts diff --git a/src/app/styles/theme.ts b/src/styles/theme.ts similarity index 100% rename from src/app/styles/theme.ts rename to src/styles/theme.ts diff --git a/src/styles/variables.scss b/src/styles/variables.scss new file mode 100644 index 00000000..2609a9e2 --- /dev/null +++ b/src/styles/variables.scss @@ -0,0 +1,62 @@ +// colors +$primaryBlue-100: #3498db; +$primaryBlue-200: #1967d6; +$primaryBlue-300: #1251aa; + +$secondaryGray-50: #f9fafb; +$secondaryGray-100: #f3f4f6; +$secondaryGray-200: #e5e7eb; +$secondaryGray-400: #9ca3af; +$secondaryGray-500: #6b7280; +$secondaryGray-600: #4b5563; +$secondaryGray-700: #374151; +$secondaryGray-800: #1f2937; +$secondaryGray-900: #111827; + +$white: #ffffff; +$black: #000000; +$error: #f74747; + +// font sizes +@mixin apply-font($font-map, $font-weight: 400) { + font-size: map-get($font-map, size); + line-height: map-get($font-map, line-height); + font-weight: $font-weight; +} + +$font-12: ( + size: 12px, + line-height: 18px, +); +$font-13: ( + size: 13px, + line-height: 22px, +); +$font-14: ( + size: 14px, + line-height: 24px, +); +$font-16: ( + size: 16px, + line-height: 26px, +); +$font-18: ( + size: 18px, + line-height: 26px, +); +$font-20: ( + size: 20px, + line-height: 32px, +); +$font-24: ( + size: 24px, + line-height: 32px, +); +$font-32: ( + size: 32px, + line-height: 42px, +); +$font-40: ( + size: 40px, + line-height: 56px, +); diff --git a/src/app/types/article.ts b/src/types/article.ts similarity index 90% rename from src/app/types/article.ts rename to src/types/article.ts index 5289338d..c77b2aaa 100644 --- a/src/app/types/article.ts +++ b/src/types/article.ts @@ -1,5 +1,5 @@ export interface GetArticleType { - totalCount: number | null + totalCount: number list: GetArticleIdType[] } diff --git a/src/app/types/comment.ts b/src/types/comment.ts similarity index 100% rename from src/app/types/comment.ts rename to src/types/comment.ts diff --git a/src/app/types/product.ts b/src/types/product.ts similarity index 100% rename from src/app/types/product.ts rename to src/types/product.ts diff --git a/src/app/types/theme.d.ts b/src/types/theme.d.ts similarity index 100% rename from src/app/types/theme.d.ts rename to src/types/theme.d.ts diff --git a/src/app/utils/datetime.ts b/src/utils/datetime.ts similarity index 100% rename from src/app/utils/datetime.ts rename to src/utils/datetime.ts diff --git a/tsconfig.json b/tsconfig.json index b20a53a0..7355788f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -13,24 +17,37 @@ "isolatedModules": true, "jsx": "preserve", "incremental": true, - "typeRoots": ["./node_modules/@types", "src/app/types"], + "typeRoots": [ + "./node_modules/@types", + "src/types" + ], "plugins": [ { "name": "next" } ], "paths": { - "@/*": ["./src/*"] + "@/*": [ + "./src/*" + ] } }, "include": [ "next-env.d.ts", "src/**/*", - "src/app/types/**/*", + "src/types/**/*", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", - "src/app/styles" + "src/styles", + "src/utils", + "src/lib/api/service/authService.jsx", + "src/lib/api/service/imageService.jsx", + "src/lib/api/service/userService.jsx", + "src/components/domain/LoginAndSignup", + "babel.config.js" ], - "exclude": ["node_modules"] + "exclude": [ + "node_modules" + ] } From a53ead29b36fac895b59c3f84f3179bf4a0829d6 Mon Sep 17 00:00:00 2001 From: ramong26 Date: Wed, 21 May 2025 15:41:24 +0900 Subject: [PATCH 2/5] =?UTF-8?q?style:=EC=A0=84=EC=B2=B4=20scss=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/Items/BestItems.module.scss | 85 +++++++ src/app/Items/BestItems.tsx | 122 ++-------- src/app/Items/RecentItems.module.scss | 73 ++++++ src/app/Items/RecentItems.tsx | 139 +++-------- .../ItemsDetailDescription.module.scss | 144 ++++++++++++ .../[productId]/ItemsDetailDescription.tsx | 218 ++++-------------- .../ItemsDetailQuestionArrary.module.scss | 126 ++++++++++ .../[productId]/ItemsDetailQuestionArrary.tsx | 198 ++++------------ .../ItemsDetailQuestionTextarea.module.scss | 42 ++++ .../ItemsDetailQuestionTextarea.tsx | 75 ++---- src/app/Items/[productId]/page.tsx | 55 +---- .../Items/[productId]/productId.module.scss | 34 +++ src/app/Items/items.module.scss | 167 ++++++++++++++ src/app/Items/page.tsx | 218 +++--------------- src/app/additem/additem.module.scss | 56 +++++ src/app/additem/page.tsx | 172 +++++++------- src/app/boards/BestBoards.module.scss | 79 +++++++ src/app/boards/BestBoards.tsx | 134 +++-------- src/app/boards/BoardDetailArrary.module.scss | 65 ++++++ src/app/boards/BoardDetailArrary.tsx | 102 ++------ src/app/boards/BoardList.module.scss | 87 +++++++ src/app/boards/BoardList.tsx | 127 ++-------- src/app/boards/boards.module.scss | 12 + src/app/boards/page.tsx | 19 +- src/app/signup/page.tsx | 107 --------- src/hooks/useInfiniteScroll.ts | 12 +- 26 files changed, 1332 insertions(+), 1336 deletions(-) create mode 100644 src/app/Items/BestItems.module.scss create mode 100644 src/app/Items/RecentItems.module.scss create mode 100644 src/app/Items/[productId]/ItemsDetailDescription.module.scss create mode 100644 src/app/Items/[productId]/ItemsDetailQuestionArrary.module.scss create mode 100644 src/app/Items/[productId]/ItemsDetailQuestionTextarea.module.scss create mode 100644 src/app/Items/[productId]/productId.module.scss create mode 100644 src/app/Items/items.module.scss create mode 100644 src/app/additem/additem.module.scss create mode 100644 src/app/boards/BestBoards.module.scss create mode 100644 src/app/boards/BoardDetailArrary.module.scss create mode 100644 src/app/boards/BoardList.module.scss create mode 100644 src/app/boards/boards.module.scss diff --git a/src/app/Items/BestItems.module.scss b/src/app/Items/BestItems.module.scss new file mode 100644 index 00000000..1502726f --- /dev/null +++ b/src/app/Items/BestItems.module.scss @@ -0,0 +1,85 @@ +@import '../../styles//variables.scss'; + +.bone { + height: 42.6rem; + width: auto; + margin-bottom: 4rem; + @media (max-width: 1023px) { + height: 48.2rem; + } + @media (max-width: 743px) { + margin-bottom: 2.4rem; + } +} +.title { + @include apply-font($font-20, 700); + color: $secondaryGray-900; + margin-bottom: 1.6rem; +} + +.best-item { + display: flex; + height: 37.8rem; + flex-direction: column; + cursor: pointer; + justify-content: space-between; + img { + width: 28.2rem; + height: 28.2rem; + border-radius: 1rem; + } + @media (max-width: 1023px) { + height: 43.4rem; + img { + width: 34.3rem; + height: 34.3rem; + } + } +} +.best-item-image { + width: 17.625rem; + height: 17.625rem; + border-radius: 1rem; + @media (max-width: 1023px) { + height: 21.437rem; + width: 100%; + } +} +.best-items-display { + display: flex; + align-items: center; + justify-content: space-between; + @media (max-width: 1023px) { + gap: 10px; + } +} +.product-description { + width: 100%; + height: 8rem; + margin-top: 1.6rem; + display: flex; + flex-direction: column; + justify-content: space-between; + @media (max-width: 1023px) { + margin-top: 0.625rem; + } +} +.product-name { + @include apply-font($font-14, 500); + color: $secondaryGray-800; +} +.product-price { + @include apply-font($font-16, 700); + color: $secondaryGray-800; +} +.product-favorite-count { + @include apply-font($font-12, 500); + color: $secondaryGray-600; + display: flex; + align-items: center; + gap: 0.4rem; + img { + width: 1.6rem; + height: 1.6rem; + } +} diff --git a/src/app/Items/BestItems.tsx b/src/app/Items/BestItems.tsx index 70ba0298..d9235641 100644 --- a/src/app/Items/BestItems.tsx +++ b/src/app/Items/BestItems.tsx @@ -1,15 +1,13 @@ 'use client' import React, { useEffect, useState } from 'react' import Link from 'next/link' +import Image from 'next/image' import { GetProductIdTypes } from '../../types/product' import HeartInactive from '../../../public/assets/image/heart_inactive.png' -import { theme } from '../../styles/theme' -import { textStyle } from '../../styles/textStyle' -import styled from 'styled-components' -import Image from 'next/image' +import styles from './BestItems.module.scss' interface BestItemsProps { products: GetProductIdTypes[] @@ -36,14 +34,16 @@ const BestItems = ({ products }: BestItemsProps) => { }, []) return ( - - 베스트 상품 +
+ {/* 점 표기법이 아니고 [] 사용한 이유는 클래스명에 - 하이픈이 있을 수도 있고 동적일 때도 []를 쓰지만 여기서는 하이픈 이유 때문에 []를 씀*/} +
베스트 상품
- +
{list.slice(0, itemsDisplay).map((product) => ( - - + 0 ? product.images[0] @@ -51,106 +51,22 @@ const BestItems = ({ products }: BestItemsProps) => { } alt={product.name} /> - - {product.name} - +
+
{product.name}
+
{product.price.toLocaleString('ko-KR')}원 - - +
+
HeartInactive {product.favoriteCount} - - - +
+
+
))} -
- +
+
) } export default BestItems - -const Bone = styled.div` - height: 42.6rem; - width: auto; - margin-bottom: 4rem; - @media (max-width: 1023px) { - height: 48.2rem; - } - @media (max-width: 743px) { - margin-bottom: 2.4rem; - } -` -const Title = styled.div` - ${(props) => textStyle(20, 700)(props)} - color: ${theme.colors.SecondaryGray[900]}; - margin-bottom: 1.6rem; -` - -const BestItem = styled.div` - display: flex; - height: 37.8rem; - flex-direction: column; - cursor: pointer; - justify-content: space-between; - img { - width: 28.2rem; - height: 28.2rem; - border-radius: 1rem; - } - @media (max-width: 1023px) { - height: 43.4rem; - img { - width: 34.3rem; - height: 34.3rem; - } - } -` -const BestItemImage = styled.img` - width: 17.625rem; - height: 17.625rem; - border-radius: 1rem; - @media (max-width: 1023px) { - height: 21.437rem; - width: 100%; - } -` -const BestItemsDisplay = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - @media (max-width: 1023px) { - gap: 10px; - } -` -const ProductDescription = styled.div` - width: 100%; - height: 8rem; - margin-top: 1.6rem; - display: flex; - flex-direction: column; - justify-content: space-between; - @media (max-width: 1023px) { - margin-top: 0.625rem; - } -` -const ProductName = styled.div` - ${(props) => textStyle(14, 500)(props)} - color: ${theme.colors.SecondaryGray[800]}; -` -const ProductPrice = styled.div` - ${(props) => textStyle(16, 700)(props)} - color: ${theme.colors.SecondaryGray[800]}; -` -const ProductFavoriteCount = styled.div` - ${(props) => textStyle(12, 500)(props)} - color: ${theme.colors.SecondaryGray[600]}; - display: flex; - align-items: center; - gap: 0.4rem; - img { - width: 1.6rem; - height: 1.6rem; - } -` diff --git a/src/app/Items/RecentItems.module.scss b/src/app/Items/RecentItems.module.scss new file mode 100644 index 00000000..b20d5639 --- /dev/null +++ b/src/app/Items/RecentItems.module.scss @@ -0,0 +1,73 @@ +@import '../../styles//variables.scss'; + +.recent-item { + height: 67.4rem; + @media (max-width: 743px) { + height: fit-content; + } +} +.recent-item-key { + display: flex; + height: 31.7rem; + width: 22.1rem; + justify-content: center; + flex-direction: column; + img { + width: 22.1rem; + height: 22.1rem; + border-radius: 1rem; + } + @media (max-width: 743px) { + height: 26.4rem; + width: 16.8rem; + img { + width: 16.8rem; + height: 16.8rem; + border-radius: 1rem; + } + } +} +.recent-item-image { + width: inherit; + height: 100%; + border-radius: 1rem; +} +.recent-items-display { + display: flex; + align-items: center; + justify-content: space-between; + flex-direction: row; + flex-wrap: wrap; + align-content: flex-start; + row-gap: 4rem; + @media (max-width: 743px) { + row-gap: 2rem; + } +} +.product-description { + width: 100%; + height: 8rem; + margin-top: 1.6rem; + display: flex; + flex-direction: column; + justify-content: space-between; +} +.product-name { + @include apply-font($font-14, 500); + color: $secondaryGray-800; +} +.product-price { + @include apply-font($font-16, 700); + color: $secondaryGray-800; +} +.product-favorite-count { + @include apply-font($font-12, 500); + color: $secondaryGray-600; + display: flex; + align-items: center; + gap: 0.4rem; + img { + width: 1.6rem; + height: 1.6rem; + } +} diff --git a/src/app/Items/RecentItems.tsx b/src/app/Items/RecentItems.tsx index f5343761..a7e5bcd0 100644 --- a/src/app/Items/RecentItems.tsx +++ b/src/app/Items/RecentItems.tsx @@ -9,9 +9,7 @@ import { GetProductIdTypes } from '../../types/product' import HeartInactive from '../../../public/assets/image/heart_inactive.png' import NoImage from '../../../public/assets/image/no_image.png' -import styled from 'styled-components' -import { theme } from '../../styles/theme' -import { textStyle } from '../../styles/textStyle' +import styles from './RecentItems.module.scss' const RecentItems = ({ products }: { products: GetProductIdTypes[] }) => { const [itemsDisplay, setItemsDisplay] = useState(1) @@ -32,113 +30,36 @@ const RecentItems = ({ products }: { products: GetProductIdTypes[] }) => { return () => window.removeEventListener('resize', handleReasize) }, []) return ( - <> - - - {products.slice(0, itemsDisplay).map((product) => ( - - - 0 - ? product.images[0] - : NoImage.src - } - alt={product.name} - /> - - {product.name} - - {product.price.toLocaleString('ko-KR')}원 - - - HeartInactive - {product.favoriteCount} - - - - - ))} - - - +
+
+ {products.slice(0, itemsDisplay).map((product) => ( + +
+ 0 + ? product.images[0] + : NoImage.src + } + alt={product.name} + /> +
+
{product.name}
+
+ {product.price.toLocaleString('ko-KR')}원 +
+
+ HeartInactive + {product.favoriteCount} +
+
+
+ + ))} +
+
) } export default RecentItems - -const RecentItem = styled.div` - height: 67.4rem; - @media (max-width: 743px) { - height: fit-content; - } -` -const RecentItemKey = styled.div` - display: flex; - height: 31.7rem; - width: 22.1rem; - justify-content: center; - flex-direction: column; - img { - width: 22.1rem; - height: 22.1rem; - border-radius: 1rem; - } - @media (max-width: 743px) { - height: 26.4rem; - width: 16.8rem; - img { - width: 16.8rem; - height: 16.8rem; - border-radius: 1rem; - } - } -` -const RecentItemImage = styled.img` - width: inherit; - height: 100%; - border-radius: 1rem; -` -const RecentItemsDisplay = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - flex-direction: row; - flex-wrap: wrap; - align-content: flex-start; - row-gap: 4rem; - @media (max-width: 743px) { - row-gap: 2rem; - } -` -const ProductDescription = styled.div` - width: 100%; - height: 8rem; - margin-top: 1.6rem; - display: flex; - flex-direction: column; - justify-content: space-between; -` -const ProductName = styled.div` - ${(props) => textStyle(14, 500)(props)} - color: ${theme.colors.SecondaryGray[800]}; -` -const ProductPrice = styled.div` - ${(props) => textStyle(16, 700)(props)} - color: ${theme.colors.SecondaryGray[800]}; -` -const ProductFavoriteCount = styled.div` - ${(props) => textStyle(12, 500)(props)} - color: ${theme.colors.SecondaryGray[600]}; - display: flex; - align-items: center; - gap: 0.4rem; - img { - width: 1.6rem; - height: 1.6rem; - } -` diff --git a/src/app/Items/[productId]/ItemsDetailDescription.module.scss b/src/app/Items/[productId]/ItemsDetailDescription.module.scss new file mode 100644 index 00000000..c149cb41 --- /dev/null +++ b/src/app/Items/[productId]/ItemsDetailDescription.module.scss @@ -0,0 +1,144 @@ +@import '../../../styles/variables'; +.bone { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: space-between; + padding-bottom: 4rem; + border-bottom: 1px solid $secondaryGray-200; + @media (max-width: 1023px) { + align-items: flex-start; + } + @media (max-width: 743px) { + flex-direction: column; + padding-bottom: 2.4rem; + } +} +.product-image { + width: 48.6rem; + height: 48.6rem; + border-radius: 1rem; + @media (max-width: 1023px) { + width: 34rem; + height: 34rem; + } + @media (max-width: 743px) { + margin-bottom: 1rem; + } +} +.title-description { + width: 69rem; + height: 49.6rem; + position: relative; + @media (max-width: 1023px) { + width: 34rem; + height: 48.4rem; + } + @media (max-width: 743px) { + height: 49.2rem; + } +} +.product-title-wrapper { + height: 9.8rem; + display: flex; + justify-content: space-evenly; + flex-direction: column; + border-bottom: 1px solid $secondaryGray-200; + @media (max-width: 743px) { + height: 6.6rem; + justify-content: flex-start; + } +} +.product-title { + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; +} +.product-name { + @include apply-font($font-24, 600); + color: $secondaryGray-800; + @media (max-width: 1023px) { + @include apply-font($font-20, 600); + } + @media (max-width: 743px) { + @include apply-font($font-16, 600); + } +} +.product-price { + @include apply-font($font-40, 600); + color: $secondaryGray-800; + @media (max-width: 1023px) { + @include apply-font($font-32, 600); + } + @media (max-width: 743px) { + @include apply-font($font-24, 600); + } +} +.product-description-wrapper { + margin: 2.4rem 0; + height: auto; + @media (max-width: 1023px) { + margin: 1rem 0 2.3rem; + } +} +.product-description-text { + @include apply-font($font-16, 600); + color: $secondaryGray-600; + + margin-bottom: 1rem; + @media (max-width: 1023px) { + @include apply-font($font-14, 600); + } +} +.product-description { + @include apply-font($font-16, 400); + color: $secondaryGray-600; +} +.product-tag-wrapper { + @include apply-font($font-16, 400); + color: $secondaryGray-600; + @media (max-width: 1023px) { + @include apply-font($font-14, 600); + } + @media (max-width: 743px) { + margin-bottom: 2.5rem; + } +} +.product-tag { + display: flex; + gap: 0.8rem; + flex-direction: row; + flex-wrap: wrap; +} +.product-footer { + width: 100%; + position: absolute; + bottom: 1px; + display: flex; + align-items: center; + justify-content: space-between; +} +.product-footer-left { + align-items: center; + justify-content: center; + display: flex; + gap: 1.6rem; +} +.product-footer-right { + display: flex; + gap: 0.6rem; + align-items: center; + padding: 0.4rem 1.2rem; + border: 1px solid $secondaryGray-200; + border-radius: 3.5rem; +} +.owner-nickname { + @include apply-font($font-14, 500); + color: $secondaryGray-600; +} +.updatedat { + @include apply-font($font-14, 400); + color: $secondaryGray-400; +} diff --git a/src/app/Items/[productId]/ItemsDetailDescription.tsx b/src/app/Items/[productId]/ItemsDetailDescription.tsx index 6ce8f163..4eaca071 100644 --- a/src/app/Items/[productId]/ItemsDetailDescription.tsx +++ b/src/app/Items/[productId]/ItemsDetailDescription.tsx @@ -2,6 +2,7 @@ import React from 'react' import { useParams } from 'next/navigation' +import Image from 'next/image' import { useGetProductId } from '../../hooks/useProductService' import Tag from '../../../components/common/Tag' @@ -11,10 +12,7 @@ import Setting from '../../../../public/assets/svg/setting_icon.svg' import ProfileIcon from '../../../../public/assets/svg/profile_icon.svg' import HeartInactive from '../../../../public/assets/image/heart_inactive.png' -import styled from 'styled-components' -import { theme } from '../../../styles/theme' -import { textStyle } from '../../../styles/textStyle' -import Image from 'next/image' +import styles from './ItemsDetailDescription.module.scss' const ItemsDetailDescription = () => { const params = useParams() @@ -27,188 +25,56 @@ const ItemsDetailDescription = () => { return
상품 정보를 불러오는 중입니다...
} return ( - - - - - - {product.name} +
+ {product.name} +
+
+
+
{product.name}
상품설정버튼 - - {product.price.toLocaleString()}원 - - - 상품 소개 - {product.description} - - - 상품 태그 - +
+
+ {product.price.toLocaleString()}원 +
+
+
+
상품 소개
+
+ {product.description} +
+
+
+
상품 태그
+
{product.tags?.map((tag, index) => ( ))} - - - - +
+
+
+
프로필아이콘
- {product.ownerNickname} - {formatDate(product.createdAt)} +
+ {product.ownerNickname} +
+
+ {formatDate(product.createdAt)} +
- - +
+
하트활성화/비활성화
{product.favoriteCount}
- - - - +
+
+
+
) } export default ItemsDetailDescription - -const Bone = styled.div` - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: space-between; - padding-bottom: 4rem; - border-bottom: 1px solid ${theme.colors.SecondaryGray[200]}; - @media (max-width: 1023px) { - align-items: flex-start; - } - @media (max-width: 743px) { - flex-direction: column; - padding-bottom: 2.4rem; - } -` -const ProductImage = styled.img` - width: 48.6rem; - height: 48.6rem; - border-radius: 1rem; - @media (max-width: 1023px) { - width: 34rem; - height: 34rem; - } - @media (max-width: 743px) { - margin-bottom: 1rem; - } -` -const TitleDescription = styled.div` - width: 69rem; - height: 49.6rem; - position: relative; - @media (max-width: 1023px) { - width: 34rem; - height: 48.4rem; - } - @media (max-width: 743px) { - height: 49.2rem; - } -` -const ProductTitleWrapper = styled.div` - height: 9.8rem; - display: flex; - justify-content: space-evenly; - flex-direction: column; - border-bottom: 1px solid ${theme.colors.SecondaryGray[200]}; - @media (max-width: 743px) { - height: 6.6rem; - justify-content: flex-start; - } -` -const ProductTitle = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - cursor: pointer; -` -const ProductName = styled.div` - ${(props) => textStyle(24, 600)(props)} - color: ${theme.colors.SecondaryGray[800]}; - @media (max-width: 1023px) { - ${(props) => textStyle(20, 600)(props)} - } - @media (max-width: 743px) { - ${(props) => textStyle(16, 600)(props)} - } -` -const ProductPrice = styled.div` - ${(props) => textStyle(40, 600)(props)} - color: ${theme.colors.SecondaryGray[800]}; - @media (max-width: 1023px) { - ${(props) => textStyle(32, 600)(props)} - } - @media (max-width: 743px) { - ${(props) => textStyle(24, 600)(props)} - } -` -const ProductDescriptionWrapper = styled.div` - margin: 2.4rem 0; - height: auto; - @media (max-width: 1023px) { - margin: 1rem 0 2.3rem; - } -` -const ProductDescriptionText = styled.div` - ${(props) => textStyle(16, 600)(props)} - color: ${theme.colors.SecondaryGray[600]}; - - margin-bottom: 1rem; - @media (max-width: 1023px) { - ${(props) => textStyle(14, 600)(props)} - } -` -const ProductDescription = styled.div` - ${(props) => textStyle(16, 400)(props)} - color: ${theme.colors.SecondaryGray[600]}; -` -const ProductTagWrapper = styled.div` - ${(props) => textStyle(16, 400)(props)} - color: ${theme.colors.SecondaryGray[600]}; - @media (max-width: 1023px) { - ${(props) => textStyle(14, 600)(props)} - } - @media (max-width: 743px) { - margin-bottom: 2.5rem; - } -` -const ProductTag = styled.div` - display: flex; - gap: 0.8rem; - flex-direction: row; - flex-wrap: wrap; -` -const ProductFooter = styled.div` - width: 100%; - position: absolute; - bottom: 1px; - display: flex; - align-items: center; - justify-content: space-between; -` -const ProductFooterLeft = styled.div` - align-items: center; - justify-content: center; - display: flex; - gap: 1.6rem; -` -const ProductFooterRight = styled.div` - display: flex; - gap: 0.6rem; - align-items: center; - padding: 0.4rem 1.2rem; - border: 1px solid ${theme.colors.SecondaryGray[200]}; - border-radius: 3.5rem; -` -const OwnerNickname = styled.div` - ${(props) => textStyle(14, 500)(props)} - color: ${theme.colors.SecondaryGray[600]}; -` -const UpdatedAt = styled.div` - ${(props) => textStyle(14, 400)(props)} - color: ${theme.colors.SecondaryGray[400]}; -` diff --git a/src/app/Items/[productId]/ItemsDetailQuestionArrary.module.scss b/src/app/Items/[productId]/ItemsDetailQuestionArrary.module.scss new file mode 100644 index 00000000..f12fe0ff --- /dev/null +++ b/src/app/Items/[productId]/ItemsDetailQuestionArrary.module.scss @@ -0,0 +1,126 @@ +@import '../../../styles/variables'; + +.edit-bone { + position: relative; + margin: 1rem 0 7rem; + z-index: 1; +} +.editing-wrapper { + position: absolute; + width: 100%; + bottom: -212px; + @media (max-width: 1023px) { + bottom: -190px; + } +} +.button-wrapper { + width: max-content; +} +.edit-button { + padding: 0.8rem 2.3rem; + width: max-content; +} +.button-edit-wrapper { + display: flex; + align-items: center; + justify-content: end; + margin: 1.6rem 0 2.4rem; + gap: 0.4rem; +} +.edit-cancel-button { + @include apply-font($font-16, 600); + color: $secondaryGray-500; + width: 68px; + height: 47px; + display: flex; + align-items: center; + justify-content: center; +} + +.bone { + border-bottom: 1px solid $secondaryGray-200; + display: flex; + align-items: end; + justify-content: space-between; + height: 10rem; + position: relative; + padding-bottom: 1.2rem; + + margin-top: 2.4rem; + @media (max-width: 1023px) { + margin-top: 0; + } +} + +.question-content { + @include apply-font($font-14, 400); + color: $secondaryGray-800; + margin-bottom: 2.4rem; +} +.user-profile-image-wrapper { + display: flex; + gap: 0.5rem; + img { + border-radius: 50%; + } +} +.user-profile-image-right { + display: flex; + flex-direction: column; + justify-content: space-between; +} +.user-profile-name { + @include apply-font($font-12, 400); + color: $secondaryGray-600; +} +.diff-date { + @include apply-font($font-12, 400); + color: $secondaryGray-400; +} +.setting-button-wrapper { + position: relative; + top: -42px; + cursor: pointer; +} + +.select-option { + position: absolute; + top: 1.8rem; + left: -124px; + border: 1px solid #cccccc; + border-radius: 12px; + background-color: #ffffff; + color: #181818; + font-weight: 400; + font-size: 16px; + line-height: 26px; + max-height: 300px; + overflow-y: auto; + z-index: 10; + list-style: none; + padding: 0; + display: flex; + align-items: center; + flex-direction: column; + justify-content: space-around; + @media (max-width: 743px) { + position: absolute; + top: 30px; + left: -110px; + z-index: 1; + width: 130px; + height: 84px; + } +} +.option { + @include apply-font($font-16, 400); + color: $secondaryGray-500; + padding: 12px 40px; + cursor: pointer; + &:hover { + background-color: #f6f6f6; + } + @media (max-width: 743px) { + padding: 7px 35px; + } +} diff --git a/src/app/Items/[productId]/ItemsDetailQuestionArrary.tsx b/src/app/Items/[productId]/ItemsDetailQuestionArrary.tsx index 435227a5..c8b98dbc 100644 --- a/src/app/Items/[productId]/ItemsDetailQuestionArrary.tsx +++ b/src/app/Items/[productId]/ItemsDetailQuestionArrary.tsx @@ -12,9 +12,7 @@ import { formatDate } from '../../../utils/datetime' import Setting from '../../../../public/assets/svg/setting_icon.svg' -import styled from 'styled-components' -import { theme } from '../../../styles/theme' -import { textStyle } from '../../../styles/textStyle' +import styles from './ItemsDetailQuestionArrary.module.scss' interface ItemsDetailQuestionArraryProps { productQuestion: PostCommentType @@ -68,8 +66,8 @@ const ItemsDetailQuestionArrary = ({ return ( <> {isEditingState ? ( - - +
+
{/*로그인을 하지 않아 토큰?전달이 되지 않은 상태*/} setUserComment(e.target.value)} /> - - +
+ +
+ +
+
+
+
) : ( <> )} - +
- {productQuestion.content} - +
+ {productQuestion.content} +
+
{/*이미지가 없을 경우 기본 이미지 적용*/} - - +
+
{productQuestion.writer.nickname} - +
{/*날짜 차이가 31일을 넘길 경우 createAt을 출력*/} - +
{diffDate(productQuestion.createdAt) > 31 ? ( <> {formatDate(productQuestion.createdAt)} @@ -122,151 +129,28 @@ const ItemsDetailQuestionArrary = ({ 일 전 )} - - - +
+
+
- +
수정, 삭제 선택 버튼 {isDropDownOpen && ( - - - - +
    +
  • + 수정하기 +
  • +
  • 삭제하기
  • +
)} - - +
+
) } export default ItemsDetailQuestionArrary - -const EditBone = styled.div` - position: relative; - margin: 1rem 0 7rem; - z-index: 1; -` -const EditingWrapper = styled.div` - position: absolute; - width: 100%; - bottom: -212px; - @media (max-width: 1023px) { - bottom: -190px; - } -` -const ButtonWrapper = styled.div` - width: max-content; -` -const EditButton = styled(Button)` - padding: 0.8rem 2.3rem; - width: max-content; -` -const ButtonEditWrapper = styled.div` - display: flex; - align-items: center; - justify-content: end; - margin: 1.6rem 0 2.4rem; - gap: 0.4rem; -` -const EditCalcelButton = styled.button` - ${(props) => textStyle(16, 600)(props)} - color: ${theme.colors.SecondaryGray[500]}; - width: 68px; - height: 47px; - display: flex; - align-items: center; - justify-content: center; -` - -const Bone = styled.div` - border-bottom: 1px solid ${theme.colors.SecondaryGray[200]}; - display: flex; - align-items: end; - justify-content: space-between; - height: 10rem; - position: relative; - padding-bottom: 1.2rem; - - margin-top: 2.4rem; - @media (max-width: 1023px) { - margin-top: 0; - } -` - -const QuestionContent = styled.div` - ${(props) => textStyle(14, 400)(props)} - color: ${theme.colors.SecondaryGray[800]}; - margin-bottom: 2.4rem; -` -const UserProfileImageWrapper = styled.div` - display: flex; - gap: 0.5rem; - img { - border-radius: 50%; - } -` -const UserProfileImageRight = styled.div` - display: flex; - flex-direction: column; - justify-content: space-between; -` -const UserProfileName = styled.div` - ${(props) => textStyle(12, 400)(props)} - color: ${theme.colors.SecondaryGray[600]}; -` -const DiffDate = styled.div` - ${(props) => textStyle(12, 400)(props)} - color: ${theme.colors.SecondaryGray[400]}; -` -const SettingButtonWrapper = styled.div` - position: relative; - top: -42px; - cursor: pointer; -` - -const SelectOption = styled.ul` - position: absolute; - top: 1.8rem; - left: -124px; - border: 1px solid #cccccc; - border-radius: 12px; - background-color: #ffffff; - color: #181818; - font-weight: 400; - font-size: 16px; - line-height: 26px; - max-height: 300px; - overflow-y: auto; - z-index: 10; - list-style: none; - padding: 0; - display: flex; - align-items: center; - flex-direction: column; - justify-content: space-around; - @media (max-width: 743px) { - position: absolute; - top: 30px; - left: -110px; - z-index: 1; - width: 130px; - height: 84px; - } -` -const Option = styled.li` - ${(props) => textStyle(16, 400)(props)} - color: ${theme.colors.SecondaryGray[500]}; - padding: 12px 40px; - cursor: pointer; - &:hover { - background-color: #f6f6f6; - } - @media (max-width: 743px) { - padding: 7px 35px; - } -` diff --git a/src/app/Items/[productId]/ItemsDetailQuestionTextarea.module.scss b/src/app/Items/[productId]/ItemsDetailQuestionTextarea.module.scss new file mode 100644 index 00000000..5de50885 --- /dev/null +++ b/src/app/Items/[productId]/ItemsDetailQuestionTextarea.module.scss @@ -0,0 +1,42 @@ +@import '../../../styles/variables'; + +.product-question-wrapper { + margin: 4rem auto 2.4rem; + @media (max-width: 1023px) { + margin: 4rem auto; + } + @media (max-width: 743px) { + margin: 2.5rem auto; + } +} +.product-question-text { + @include apply-font($font-16, 600); + color: $secondaryGray-900; + margin-bottom: 0.9rem; +} + +.button-wrapper { + width: fit-content; + margin: 1.6rem 0 0 auto; +} +.register-button { + padding: 0.8rem 2.3rem; + width: max-content; +} +.items-question-wrapper { + position: relative; + display: flex; + flex-direction: column; +} + +.inquiry-empty-wrapper { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} +.inquiry-empty-text { + @include apply-font($font-16, 400); + color: $secondaryGray-400; + margin-top: 0.5rem; +} diff --git a/src/app/Items/[productId]/ItemsDetailQuestionTextarea.tsx b/src/app/Items/[productId]/ItemsDetailQuestionTextarea.tsx index a5a8f032..b43e1087 100644 --- a/src/app/Items/[productId]/ItemsDetailQuestionTextarea.tsx +++ b/src/app/Items/[productId]/ItemsDetailQuestionTextarea.tsx @@ -11,9 +11,7 @@ import Button from '../../../components/common/Button' import InquiryEmpty from '../../../../public/assets/svg/inquiry_empty.svg' -import styled from 'styled-components' -import { theme } from '../../../styles/theme' -import { textStyle } from '../../../styles/textStyle' +import styles from './ItemsDetailQuestionTextarea.module.scss' const ItemsDetailQuestionTextarea = () => { const params = useParams() // product 데어터 받기 @@ -44,9 +42,9 @@ const ItemsDetailQuestionTextarea = () => { }, []) return ( - - - 문의하기 + <> +
+
문의하기
{ value={questionText} onChange={(e) => setQuestionText(e.target.value)} /> - - + +
+
{productQuestion.list.length === 0 ? ( - +
문의가 없습니다 - 문의가 없습니다 - +
문의가 없습니다
+
) : ( <> - +
{productQuestion.list.map((question, index) => ( { isEditing={isEditing} /> ))} - +
)} - + ) } export default ItemsDetailQuestionTextarea - -const Bone = styled.div`` -const ProductQuestionWrapper = styled.div` - margin: 4rem auto 2.4rem; - @media (max-width: 1023px) { - margin: 4rem auto; - } - @media (max-width: 743px) { - margin: 2.5rem auto; - } -` -const ProductQuestionText = styled.div` - ${(props) => textStyle(16, 600)(props)} - color: ${theme.colors.SecondaryGray[900]}; - margin-bottom: 0.9rem; -` - -const ButtonWrapper = styled.div` - width: fit-content; - margin: 1.6rem 0 0 auto; -` -const RegisterButton = styled(Button)` - padding: 0.8rem 2.3rem; - width: max-content; -` -const ItemsQuestionWrapper = styled.div` - position: relative; - display: flex; - flex-direction: column; -` - -const InquiryEmptyWrapper = styled.div` - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; -` -const InquiryEmptyText = styled.div` - ${(props) => textStyle(16, 400)(props)} - color: ${theme.colors.SecondaryGray[400]}; - margin-top: 0.5rem; -` diff --git a/src/app/Items/[productId]/page.tsx b/src/app/Items/[productId]/page.tsx index c77df0a8..ba93e368 100644 --- a/src/app/Items/[productId]/page.tsx +++ b/src/app/Items/[productId]/page.tsx @@ -1,71 +1,38 @@ 'use client' import React from 'react' -import ItemsNavVar from '../../../components/domain/Nav/ItemsNavVar' +import Image from 'next/image' +import ItemsNavVar from '../../../components/domain/Nav/ItemsNavVar' import ItemsDetailDescription from './ItemsDetailDescription' import ItemsDetailQuestionTextarea from './ItemsDetailQuestionTextarea' import Button from '../../../components/common/Button' import BackIcon from '../../../../public/assets/svg/back_icon.svg' -import styled from 'styled-components' -import { theme } from '../../../styles/theme' -import { textStyle } from '../../../styles/textStyle' -import Image from 'next/image' + +import styles from './productId.module.scss' const ItemsDetail = () => { return ( <> - +
- - + +
+ ) } export default ItemsDetail - -const Bone = styled.div` - width: 120rem; - margin: 1.5rem auto 22.2rem; - - @media (max-width: 1023px) { - width: 69.6rem; - margin: 1.5rem auto 24.3rem auto; - } - @media (max-width: 743px) { - width: 34.4rem; - margin: 1rem auto 6.5rem auto; - } -` - -const ButtonWrapper = styled.div` - display: flex; - justify-content: center; - margin: 4.6875rem auto; - width: max-content; - ${(props) => textStyle(18, 600)(props)} - color: ${theme.colors.SecondaryGray[100]}; - @media (max-width: 1023px) { - margin: 3.5rem auto 10.4375rem auto; - } - @media (max-width: 743px) { - margin: 4rem auto 2.5rem auto; - } -` -const ListButton = styled(Button)` - padding: 1.1rem 3.9rem; - width: max-content; -` diff --git a/src/app/Items/[productId]/productId.module.scss b/src/app/Items/[productId]/productId.module.scss new file mode 100644 index 00000000..a6f49f63 --- /dev/null +++ b/src/app/Items/[productId]/productId.module.scss @@ -0,0 +1,34 @@ +@import '../../../styles/variables.scss'; + +.bone { + width: 120rem; + margin: 1.5rem auto 22.2rem; + + @media (max-width: 1023px) { + width: 69.6rem; + margin: 1.5rem auto 24.3rem auto; + } + @media (max-width: 743px) { + width: 34.4rem; + margin: 1rem auto 6.5rem auto; + } +} + +.button-wrapper { + display: flex; + justify-content: center; + margin: 4.6875rem auto; + width: max-content; + @include apply-font($font-18, 600); + color: $secondaryGray-100; + @media (max-width: 1023px) { + margin: 3.5rem auto 10.4375rem auto; + } + @media (max-width: 743px) { + margin: 4rem auto 2.5rem auto; + } +} +.list-button { + padding: 1.1rem 3.9rem; + width: max-content; +} diff --git a/src/app/Items/items.module.scss b/src/app/Items/items.module.scss new file mode 100644 index 00000000..e339e6fe --- /dev/null +++ b/src/app/Items/items.module.scss @@ -0,0 +1,167 @@ +@import '../../styles/variables.scss'; + +.bone { + width: 120rem; + margin: 2.4rem auto; + @media (max-width: 1023px) { + width: 69.6rem; + } + @media (max-width: 743px) { + width: 34.4rem; + margin: 1rem auto; + } +} +.nav-var { + height: 4.2rem; + width: 100%; + margin: 0 auto 2.4rem; + display: flex; + align-items: center; + justify-content: space-between; + @media (max-width: 743px) { + flex-wrap: wrap; + margin: 0 auto 6.6rem; + } +} +.nav-title { + @include apply-font($font-20, 700); + color: $secondaryGray-900; + @media (max-width: 743px) { + height: 2.625rem; + display: flex; + align-items: center; + justify-content: center; + } +} +.nav-right-wrapper { + height: 100%; + display: flex; + align-items: center; + position: relative; + @media (max-width: 743px) { + flex-wrap: wrap; + } +} +.search-icon { + position: absolute; + right: 585px; + z-index: 10; + display: flex; + @media (max-width: 1023px) { + right: 412px; + } + @media (max-width: 743px) { + right: 308px; + top: 28px; + } +} +.register-button { + padding: 0.8rem 2.3rem; + width: max-content; +} +.nav-search { + width: 32.5rem; + height: 100%; + padding: 0.9rem 10.7rem 0.9rem 4.4rem; + border-radius: 1.2rem; + @include apply-font($font-16, 400); + color: $secondaryGray-400; + background-color: $secondaryGray-100; + border: none; + margin-right: 1.2rem; + @media (max-width: 1023px) { + width: 15.125rem; + padding: 9px 24px 9px 44px; + } + @media (max-width: 743px) { + position: relative; + top: 19px; + padding: 9px 40px 9px 44px; + width: max-content; + margin: 0; + } +} +.search-input { + border: none; + background-color: $secondaryGray-100; +} + +.button-wrapper { + margin-right: 1.2rem; + @media (max-width: 743px) { + position: relative; + width: max-content; + top: -74px; + left: 206px; + } + button { + transition: all 0.3s ease-in-out; + + &:hover { + transform: scale(1.05); + } + + &:active { + transform: scale(0.95); + } + } +} + +.pagination { + width: 30.4rem; + height: 4rem; + display: flex; + align-items: center; + justify-content: space-between; + margin: 4.3rem auto 0 auto; + @media (max-width: 1023px) { + margin: 2.5rem auto 0 auto; + } +} +.arrow-button { + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + border: 1px solid $secondaryGray-200; + background: #ffffff; + color: $secondaryGray-600; + border-radius: 40px; + height: 40px; + width: 40px; + padding: 12px; + + &:hover { + background: $primaryBlue-200; + } + + &:disabled { + background: $secondaryGray-400; + cursor: not-allowed; + } +} + +interface PageButtonProps { + $isActive: boolean; +} +.page-button { + width: 40px; + height: 40px; + border-radius: 50%; + border: 1px solid $secondaryGray-100; + background: #fff; + color: $secondaryGray-900; + cursor: pointer; + transition: background 0.2s, color 0.2s; + display: flex; + align-items: center; + justify-content: center; + &:hover { + background: $primaryBlue-200; + } + + &.active { + background-color: $primaryBlue-100; + color: $secondaryGray-50; + } +} diff --git a/src/app/Items/page.tsx b/src/app/Items/page.tsx index 9d337cf2..b3be3720 100644 --- a/src/app/Items/page.tsx +++ b/src/app/Items/page.tsx @@ -17,9 +17,7 @@ import Search from '../../../public/assets/svg/search.svg' import ArrowLeft from '../../../public/assets/svg/arrow_left.svg' import ArrowRight from '../../../public/assets/svg/arrow_right.svg' -import styled, { css } from 'styled-components' -import { theme } from '../../styles/theme' -import { textStyle } from '../../styles/textStyle' +import styles from './items.module.scss' type SelectOption = { value: string @@ -128,21 +126,31 @@ const Items = () => { return ( <> - +
- - 전체상품 - - +
+
전체상품
+
+
검색 아이콘 - - - - +
+
+ +
+
+ +
{ setSelectedOption(value) }} /> - - +
+
- - +
+
왼쪽 페이지 화살표 - +
{pages.map((page) => ( - { setCurrentPage(page) }} > {page} - +
))} - +
오른쪽 페이지 화살표 - - - +
+
+ ) } export default Items - -const Bone = styled.div` - width: 120rem; - margin: 2.4rem auto; - @media (max-width: 1023px) { - width: 69.6rem; - } - @media (max-width: 743px) { - width: 34.4rem; - margin: 1rem auto; - } -` -const NavVAr = styled.div` - height: 4.2rem; - width: 100%; - margin: 0 auto 2.4rem; - display: flex; - align-items: center; - justify-content: space-between; - @media (max-width: 743px) { - flex-wrap: wrap; - margin: 0 auto 6.6rem; - } -` -const NavTitle = styled.div` - ${(props) => textStyle(20, 700)(props)} - color: ${theme.colors.SecondaryGray[900]}; - @media (max-width: 743px) { - height: 2.625rem; - display: flex; - align-items: center; - justify-content: center; - } -` -const NavRightWrapper = styled.div` - height: 100%; - display: flex; - align-items: center; - position: relative; - @media (max-width: 743px) { - flex-wrap: wrap; - } -` -const SearchIcon = styled.div` - position: absolute; - right: 585px; - z-index: 10; - display: flex; - @media (max-width: 1023px) { - right: 412px; - } - @media (max-width: 743px) { - right: 308px; - top: 28px; - } -` -const RegisterButton = styled(Button)` - padding: 0.8rem 2.3rem; - width: max-content; -` -const NavSearch = styled.input` - width: 32.5rem; - height: 100%; - padding: 0.9rem 10.7rem 0.9rem 4.4rem; - border-radius: 1.2rem; - ${(props) => textStyle(16, 400)(props)} - color: ${theme.colors.SecondaryGray[400]}; - background-color: ${theme.colors.SecondaryGray[100]}; - border: none; - margin-right: 1.2rem; - @media (max-width: 1023px) { - width: 15.125rem; - padding: 9px 24px 9px 44px; - } - @media (max-width: 743px) { - position: relative; - top: 19px; - padding: 9px 40px 9px 44px; - width: max-content; - margin: 0; - } -` -const ButtonWrapper = styled.div` - margin-right: 1.2rem; - @media (max-width: 743px) { - position: relative; - width: max-content; - top: -74px; - left: 206px; - } - button { - transition: all 0.3s ease-in-out; - - &:hover { - transform: scale(1.05); - } - - &:active { - transform: scale(0.95); - } - } -` - -const Pagenation = styled.div` - width: 30.4rem; - height: 4rem; - display: flex; - align-items: center; - justify-content: space-between; - margin: 4.3rem auto 0 auto; - @media (max-width: 1023px) { - margin: 2.5rem auto 0 auto; - } -` -const LeftButton = styled.button` - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - border: 1px solid ${theme.colors.SecondaryGray[200]}; - background: #ffffff; - color: ${({ theme }) => theme.colors.SecondaryGray[600]}; - border-radius: 40px; - height: 40px; - width: 40px; - padding: 12px; - - &:hover { - background: ${({ theme }) => theme.colors.PrimaryBlue[200]}; - } - - &:disabled { - background: ${({ theme }) => theme.colors.SecondaryGray[400]}; - cursor: not-allowed; - } -` - -interface PageButtonProps { - $isActive: boolean -} -const PageButton = styled.button` - width: 40px; - height: 40px; - border-radius: 50%; - border: 1px solid ${theme.colors.SecondaryGray[100]}; - &:hover { - background: ${({ theme }) => theme.colors.PrimaryBlue[200]}; - } - - ${({ $isActive, theme }) => - $isActive && - css` - background-color: ${theme.colors.PrimaryBlue[100]}; - color: ${theme.colors.SecondaryGray[50]}; - `} -` diff --git a/src/app/additem/additem.module.scss b/src/app/additem/additem.module.scss new file mode 100644 index 00000000..66e9c3c5 --- /dev/null +++ b/src/app/additem/additem.module.scss @@ -0,0 +1,56 @@ +@import '../../styles/variables.scss'; + +.bone { + width: 120rem; + display: flex; + align-items: center; + margin: 1.5rem auto 6.9rem auto; + flex-direction: column; + @media (max-width: 1023px) { + width: 69.6rem; + margin-bottom: 7.8rem; + } + @media (max-width: 743px) { + width: 34.6rem; + margin-bottom: 7rem; + } +} +.header { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + margin-bottom: 2.4rem; +} +.product-register { + @include apply-font($font-20, 700); + color: $secondaryGray-800; +} +.button-wrapper { + width: max-content; +} +.register-button { + padding: 0.8rem 2.3rem; + width: max-content; +} +.main { + width: 100%; +} +.display-wrapper { + width: 100%; + margin-bottom: 3.2rem; + @media (max-width: 743px) { + margin-bottom: 2.4rem; + } +} +.product-text { + @include apply-font($font-18, 700); + color: $secondaryGray-800; + margin-bottom: 1.6rem; +} +.tag-display { + display: flex; + gap: 0.75rem; + width: 100%; + flex-wrap: wrap; +} diff --git a/src/app/additem/page.tsx b/src/app/additem/page.tsx index 1b6055d3..f7d6a5dd 100644 --- a/src/app/additem/page.tsx +++ b/src/app/additem/page.tsx @@ -7,9 +7,7 @@ import ButtonImage from '../../components/common/ButtonImageInput' import TextInputPlaceholder from '../../components/common/TextInputPlaceholder' import Tag from '../../components/common/Tag' -import styled from 'styled-components' -import { theme } from '../../styles/theme' -import { textStyle } from '../../styles/textStyle' +import styles from './additem.module.scss' const AddItem = () => { const [productName, setProductName] = useState('') @@ -46,30 +44,34 @@ const AddItem = () => { return ( <> - -
- 싱품 등록하기 - - +
+
+
상품 등록하기
+
+
-
- - 상품 이미지 + + + +
+
+
상품 이미지
- - - 상품명 +
+
+
상품명
setProductName(e.target.value)} /> - - - 상품 소개 +
+
+
상품 소개
{ value={productDescription} onChange={(e) => setProductDescription(e.target.value)} /> - - - 판매 가격 +
+
+
판매 가격
setProductPrice(e.target.value)} /> - - - 태그 +
+
+
태그
setTagInput(e.target.value)} onKeyDown={handleEnterDown} /> - - +
+
{productTags.map((tag, index) => ( { onClick={() => handleDeleteTag(tag)} /> ))} - -
-
+ + + ) } export default AddItem -const Bone = styled.div` - width: 120rem; - display: flex; - align-items: center; - margin: 1.5rem auto 6.9rem auto; - flex-direction: column; - @media (max-width: 1023px) { - width: 69.6rem; - margin-bottom: 7.8rem; - } - @media (max-width: 743px) { - width: 34.6rem; - margin-bottom: 7rem; - } -` -const Header = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - margin-bottom: 2.4rem; -` -const ProductRegister = styled.div` - ${(props) => textStyle(20, 700)(props)} - color: ${theme.colors.SecondaryGray[800]}; -` -const ButtonWrapper = styled.div` - width: max-content; -` -const RegisterButton = styled(Button)` - padding: 0.8rem 2.3rem; - width: max-content; -` -const Main = styled.div` - width: 100%; -` -const DisplayWrapper = styled.div` - width: 100%; - margin-bottom: 3.2rem; - @media (max-width: 743px) { - margin-bottom: 2.4rem; - } -` -const ProductText = styled.div` - ${(props) => textStyle(18, 700)(props)} - color: ${theme.colors.SecondaryGray[800]}; - margin-bottom: 1.6rem; -` -const TagDisplay = styled.div` - display: flex; - gap: 0.75rem; - width: 100%; - flex-wrap: wrap; -` +// const Bone = styled.div` +// width: 120rem; +// display: flex; +// align-items: center; +// margin: 1.5rem auto 6.9rem auto; +// flex-direction: column; +// @media (max-width: 1023px) { +// width: 69.6rem; +// margin-bottom: 7.8rem; +// } +// @media (max-width: 743px) { +// width: 34.6rem; +// margin-bottom: 7rem; +// } +// ` +// const Header = styled.div` +// display: flex; +// align-items: center; +// justify-content: space-between; +// width: 100%; +// margin-bottom: 2.4rem; +// ` +// const ProductRegister = styled.div` +// ${(props) => textStyle(20, 700)(props)} +// color: ${theme.colors.SecondaryGray[800]}; +// ` +// const ButtonWrapper = styled.div` +// width: max-content; +// ` +// const RegisterButton = styled(Button)` +// padding: 0.8rem 2.3rem; +// width: max-content; +// ` +// const Main = styled.div` +// width: 100%; +// ` +// const DisplayWrapper = styled.div` +// width: 100%; +// margin-bottom: 3.2rem; +// @media (max-width: 743px) { +// margin-bottom: 2.4rem; +// } +// ` +// const ProductText = styled.div` +// ${(props) => textStyle(18, 700)(props)} +// color: ${theme.colors.SecondaryGray[800]}; +// margin-bottom: 1.6rem; +// ` +// const TagDisplay = styled.div` +// display: flex; +// gap: 0.75rem; +// width: 100%; +// flex-wrap: wrap; +// ` diff --git a/src/app/boards/BestBoards.module.scss b/src/app/boards/BestBoards.module.scss new file mode 100644 index 00000000..eb321eed --- /dev/null +++ b/src/app/boards/BestBoards.module.scss @@ -0,0 +1,79 @@ +@import '../../styles/variables'; + +.bone-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 4rem; + @media (max-width: 1023px) { + margin-bottom: 2.4rem; + } +} +.best-boards-title { + @include apply-font($font-20, 800); + margin-bottom: 2.4rem; +} +.main-wrapper { + width: 38.4rem; + height: 16.9rem; + padding: 0 2.4rem 1.6rem 2.4rem; + border-radius: 8px; + background-color: $secondaryGray-50; + display: flex; + justify-content: space-between; + flex-direction: column; + @media (max-width: 1023px) { + width: 34rem; + height: 19.8rem; + } +} + +.description { + display: flex; + gap: 0.8rem; +} +.main-footer { + display: flex; + align-items: center; + justify-content: space-between; +} +.text-description { + @include apply-font($font-20, 600); + @media (max-width: 1023px) { + @include apply-font($font-18, 600); + } +} +.description-image { + img { + width: 7.2rem; + height: 7.2rem; + } +} +.name-heart { + display: flex; + align-items: center; + justify-content: center; + gap: 0.8rem; + height: 2.4rem; +} +.create-date { + @include apply-font($font-14, 400); + color: $secondaryGray-500; +} +.user-name { + @include apply-font($font-14, 400); + color: $secondaryGray-500; +} +.user-heart { + display: flex; + align-items: center; + img { + width: 1.6rem; + height: 1.6rem; + } +} + +.heart-count { + @include apply-font($font-14, 400); + color: $secondaryGray-500; +} diff --git a/src/app/boards/BestBoards.tsx b/src/app/boards/BestBoards.tsx index f01e2f60..36d81040 100644 --- a/src/app/boards/BestBoards.tsx +++ b/src/app/boards/BestBoards.tsx @@ -1,3 +1,4 @@ +'use client' import React, { useState, useEffect } from 'react' import Image from 'next/image' @@ -7,10 +8,7 @@ import HeartInactive from '../../../public/assets/image/heart_inactive.png' import BestBadge from '../../../public/assets/image/best_badge.png' import { formatDate } from '../../utils/datetime' -import styled from 'styled-components' -import { theme } from '../../styles/theme' -import { textStyle } from '../../styles/textStyle' - +import styles from './BestBoards.module.scss' const BestBoards = () => { const [bestPageSize, setBestPageSize] = useState(3) useEffect(() => { @@ -37,112 +35,42 @@ const BestBoards = () => { } return ( <> - 베스트 게시글 - +
베스트 게시글
+
{bestArticleList.list.map((article) => ( - - +
+ <> 베스트 게시글 오피셜 아이콘 - - - {article.content} - + +
+
+ {article.content} +
+
게시글 이미지 - - - - - {article.writer.nickname} - +
+
+
+
+
+ {article.writer.nickname} +
+
하트 비활성화/활성화 - {article.likeCount} - - - {formatDate(article.createdAt)} - - +
+ {article.likeCount} +
+
+
+
+ {formatDate(article.createdAt)} +
+
+
))} - +
) } export default BestBoards - -const BoneWrapper = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 4rem; - @media (max-width: 1023px) { - margin-bottom: 2.4rem; - } -` -const BestBoardsTitle = styled.div` - ${(props) => textStyle(20, 800)(props)} - margin-bottom:2.4rem; -` -const MainWrapper = styled.div` - width: 38.4rem; - height: 16.9rem; - padding: 0 2.4rem 1.6rem 2.4rem; - border-radius: 8px; - background-color: ${theme.colors.SecondaryGray[50]}; - display: flex; - justify-content: space-between; - flex-direction: column; - @media (max-width: 1023px) { - width: 34rem; - height: 19.8rem; - } -` -const BestIcon = styled.div`` -const Description = styled.div` - display: flex; - gap: 0.8rem; -` -const MainFooter = styled.div` - display: flex; - align-items: center; - justify-content: space-between; -` -const TextDescription = styled.div` - ${(props) => textStyle(20, 600)(props)} - @media (max-width: 1023px) { - ${(props) => textStyle(18, 600)(props)} - } -` -const DescriptionImage = styled.div` - img { - width: 7.2rem; - height: 7.2rem; - } -` -const NameHeart = styled.div` - display: flex; - align-items: center; - justify-content: center; - gap: 0.8rem; - height: 2.4rem; -` -const CreateDate = styled.div` - ${(props) => textStyle(14, 400)(props)} - color: ${theme.colors.SecondaryGray[500]}; -` -const UserName = styled.div` - ${(props) => textStyle(14, 400)(props)} - color: ${theme.colors.SecondaryGray[500]}; -` -const UserHeart = styled.div` - display: flex; - align-items: center; - img { - width: 1.6rem; - height: 1.6rem; - } -` - -const HeartCount = styled.div` - ${(props) => textStyle(14, 400)(props)} - color: ${theme.colors.SecondaryGray[500]}; -` diff --git a/src/app/boards/BoardDetailArrary.module.scss b/src/app/boards/BoardDetailArrary.module.scss new file mode 100644 index 00000000..d148657d --- /dev/null +++ b/src/app/boards/BoardDetailArrary.module.scss @@ -0,0 +1,65 @@ +@import '../../styles/variables.scss'; + +.bone { + display: flex; + flex-direction: column; + justify-content: space-between; + height: max-content; + border-bottom: 1px solid $secondaryGray-200; + position: relative; + padding-bottom: 2.4rem; + padding-right: 2rem; + margin-top: 2.4rem; + @media (max-width: 743px) { + margin-top: 0; + margin-bottom: 2.4rem; + } +} +.content-wrapper { + display: flex; + align-items: flex-start; + justify-content: space-between; + img { + width: 7.2rem; + height: 7.2rem; + } +} + +.question-content { + @include apply-font($font-20, 600); + color: $secondaryGray-800; + margin-bottom: 2.4rem; + @media (max-width: 743px) { + @include apply-font($font-18, 600); + } +} +.user-profile-image-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + img { + width: 2.6rem; + height: 2.6rem; + border-radius: 50%; + } +} +.name-profile { + display: flex; + align-items: center; + justify-content: center; + gap: 0.8rem; +} +.user-profile-name { + @include apply-font($font-12, 400); + color: $secondaryGray-600; +} +.diff-date { + @include apply-font($font-12, 400); + color: $secondaryGray-400; +} +.heart-count { + @include apply-font($font-16, 400); + color: $secondaryGray-500; + display: flex; + gap: 0.8rem; +} diff --git a/src/app/boards/BoardDetailArrary.tsx b/src/app/boards/BoardDetailArrary.tsx index 199be8a5..6e10d79f 100644 --- a/src/app/boards/BoardDetailArrary.tsx +++ b/src/app/boards/BoardDetailArrary.tsx @@ -1,6 +1,5 @@ -'use client' - import React from 'react' +import Image from 'next/image' import { GetArticleIdType } from '../../types/article' import { diffDate } from '../../utils/datetime' @@ -8,10 +7,7 @@ import { formatDate } from '../../utils/datetime' import HeartInactive from '../../../public/assets/image/heart_inactive.png' -import styled from 'styled-components' -import { theme } from '../../styles/theme' -import { textStyle } from '../../styles/textStyle' -import Image from 'next/image' +import styles from './BoardDetailArrary.module.scss' type BestBoardsProps = { article: GetArticleIdType @@ -20,15 +16,15 @@ type BestBoardsProps = { const BoardDetailArrary = ({ article }: BestBoardsProps) => { return ( <> - - - {article.content} +
+
+
{article.content}
{article.image && ( 게시글 상세 이미지 )} - - - +
+
+
{/*이미지가 없을 경우 기본 이미지 적용*/} { height={32} /> - {article.writer.nickname} +
+ {article.writer.nickname} +
{/*날짜 차이가 31일을 넘길 경우 createAt을 출력*/} - +
{diffDate(article.createdAt) > 31 ? ( <> {formatDate(article.createdAt)} @@ -50,9 +48,9 @@ const BoardDetailArrary = ({ article }: BestBoardsProps) => { 일 전 )} - - - +
+
+
HeartInactive { height={24} /> <>{article.likeCount} - - - +
+
+
) } export default BoardDetailArrary - -const Bone = styled.div` - display: flex; - flex-direction: column; - justify-content: space-between; - height: max-content; - border-bottom: 1px solid ${theme.colors.SecondaryGray[200]}; - position: relative; - padding-bottom: 2.4rem; - padding-right: 2rem; - margin-top: 2.4rem; - @media (max-width: 743px) { - margin-top: 0; - margin-bottom: 2.4rem; - } -` -const ContentWrappeer = styled.div` - display: flex; - align-items: flex-start; - justify-content: space-between; - img { - width: 7.2rem; - height: 7.2rem; - } -` - -const QuestionContent = styled.div` - ${(props) => textStyle(20, 600)(props)} - color: ${theme.colors.SecondaryGray[800]}; - margin-bottom: 2.4rem; - @media (max-width: 743px) { - ${(props) => textStyle(18, 600)(props)} - } -` -const UserProfileImageWrapper = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - img { - width: 2.6rem; - height: 2.6rem; - border-radius: 50%; - } -` -const NameProfile = styled.div` - display: flex; - align-items: center; - justify-content: center; - gap: 0.8rem; -` -const UserProfileName = styled.div` - ${(props) => textStyle(12, 400)(props)} - color: ${theme.colors.SecondaryGray[600]}; -` -const DiffDate = styled.div` - ${(props) => textStyle(12, 400)(props)} - color: ${theme.colors.SecondaryGray[400]}; -` -const HeartCount = styled.div` - ${(props) => textStyle(16, 400)(props)} - color: ${theme.colors.SecondaryGray[500]}; - display: flex; - gap: 0.8rem; -` diff --git a/src/app/boards/BoardList.module.scss b/src/app/boards/BoardList.module.scss new file mode 100644 index 00000000..6c5919cd --- /dev/null +++ b/src/app/boards/BoardList.module.scss @@ -0,0 +1,87 @@ +@import '../../styles/variables.scss'; + +.board-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 2.4rem; + @media (max-width: 1023px) { + margin-bottom: 4.8rem; + } + @media (max-width: 743px) { + margin-bottom: 1.6rem; + } +} +.best-boards-title { + @include apply-font($font-20, 800); +} +.writer-button { + padding: 0.8rem 2.3rem; + width: max-content; +} + +.search-list { + display: flex; + align-items: center; + justify-content: space-between; + @media (max-width: 1023px) { + margin-bottom: 4rem; + } + @media (max-width: 743px) { + margin-bottom: 1.6rem; + } +} +.text-input-icon { + position: relative; +} +.nav-search { + width: 105.4rem; + height: 4.2rem; + padding: 0.9rem 8.7rem 0.9rem 4.4rem; + border-radius: 1.2rem; + @include apply-font($font-16, 400); + color: $secondaryGray-400; + background-color: $secondaryGray-100; + border: none; + margin-right: 1.2rem; + @media (max-width: 1023px) { + width: 55rem; + padding: 9px 24px 9px 44px; + } + @media (max-width: 743px) { + position: relative; + + padding: 9px 40px 9px 44px; + width: max-content; + margin: 0; + } +} +.placeholder-icon { + width: max-content; + position: absolute; + top: 10px; + left: 20px; +} + +.boards-list { + overflow-y: auto; + height: 60vh; + position: relative; +} +.items-question-wrapper { + position: relative; + display: flex; + flex-direction: column; +} + +.inquiry-empty-wrapper { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} +.inquiry-empty-text { + @include apply-font($font-16, 400); + color: $secondaryGray-400; + margin-top: 0.5rem; +} diff --git a/src/app/boards/BoardList.tsx b/src/app/boards/BoardList.tsx index 8c4ef3db..bf4db0fa 100644 --- a/src/app/boards/BoardList.tsx +++ b/src/app/boards/BoardList.tsx @@ -13,9 +13,7 @@ import Button from '../../components/common/Button' import InquiryEmpty from '../../../public/assets/svg/inquiry_empty.svg' import Search from '../../../public/assets/svg/search.svg' -import styled from 'styled-components' -import { theme } from '../../styles/theme' -import { textStyle } from '../../styles/textStyle' +import styles from './BoardList.module.scss' type SelectOption = { value: string @@ -67,21 +65,24 @@ const BoardList = () => { } return ( <> - - 게시글 - 글쓰기 - - - - +
게시글
+ + +
+
+ setSearchTerm(e.target.value)} /> - +
검색아이콘 - - +
+
{ setSelectedOption(value) }} /> - - +
+
{articleList.list?.length === 0 ? ( - +
관련된 게시글이 없습니다. - 문의가 없습니다 - +
문의가 없습니다
+
) : ( - +
{articleList.list.map((article) => ( ))} {hasMore &&
} - +
)} - +
) } export default BoardList -const BoardHeader = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 2.4rem; - @media (max-width: 1023px) { - margin-bottom: 4.8rem; - } - @media (max-width: 743px) { - margin-bottom: 1.6rem; - } -` -const BestBoardsTitle = styled.div` - ${(props) => textStyle(20, 800)(props)} -` -const WriterButton = styled(Button)` - padding: 0.8rem 2.3rem; - width: max-content; -` - -const SearchList = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - @media (max-width: 1023px) { - margin-bottom: 4rem; - } - @media (max-width: 743px) { - margin-bottom: 1.6rem; - } -` -const TextInputIcon = styled.div` - position: relative; -` -const NavSearch = styled.input` - width: 105.4rem; - height: 4.2rem; - padding: 0.9rem 8.7rem 0.9rem 4.4rem; - border-radius: 1.2rem; - ${(props) => textStyle(16, 400)(props)} - color: ${theme.colors.SecondaryGray[400]}; - background-color: ${theme.colors.SecondaryGray[100]}; - border: none; - margin-right: 1.2rem; - @media (max-width: 1023px) { - width: 55rem; - padding: 9px 24px 9px 44px; - } - @media (max-width: 743px) { - position: relative; - - padding: 9px 40px 9px 44px; - width: max-content; - margin: 0; - } -` -const PlaceholderIcon = styled.div` - width: max-content; - position: absolute; - top: 10px; - left: 20px; -` - -const BoardsList = styled.div` - overflow-y: auto; - height: 60vh; -` -const ItemsQuestionWrapper = styled.div` - position: relative; - display: flex; - flex-direction: column; -` - -const InquiryEmptyWrapper = styled.div` - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; -` -const InquiryEmptyText = styled.div` - ${(props) => textStyle(16, 400)(props)} - color: ${theme.colors.SecondaryGray[400]}; - margin-top: 0.5rem; -` diff --git a/src/app/boards/boards.module.scss b/src/app/boards/boards.module.scss new file mode 100644 index 00000000..29524c84 --- /dev/null +++ b/src/app/boards/boards.module.scss @@ -0,0 +1,12 @@ +.bone { + width: 120rem; + margin: 2.4rem auto auto auto; + @media (max-width: 1023px) { + width: 69.6rem; + margin: 2.4rem auto auto auto; + } + @media (max-width: 743px) { + width: 34.4rem; + margin: 1rem auto 6.5rem auto; + } +} diff --git a/src/app/boards/page.tsx b/src/app/boards/page.tsx index 90c3bd51..a4140904 100644 --- a/src/app/boards/page.tsx +++ b/src/app/boards/page.tsx @@ -5,31 +5,18 @@ import ItemsNavVar from '../../components/domain/Nav/ItemsNavVar' import BestBoards from './BestBoards' import BoardList from './BoardList' -import styled from 'styled-components' +import styles from './boards.module.scss' const Boards = () => { return ( <> - +
- +
) } export default Boards - -const Bone = styled.div` - width: 120rem; - margin: 2.4rem auto auto auto; - @media (max-width: 1023px) { - width: 69.6rem; - margin: 2.4rem auto auto auto; - } - @media (max-width: 743px) { - width: 34.4rem; - margin: 1rem auto 6.5rem auto; - } -` diff --git a/src/app/signup/page.tsx b/src/app/signup/page.tsx index ca3de8fa..e3d62d2d 100644 --- a/src/app/signup/page.tsx +++ b/src/app/signup/page.tsx @@ -175,110 +175,3 @@ const Signup = () => { } export default Signup - -// const Bone = styled.div` -// display: flex; -// align-items: center; -// justify-content: center; -// flex-direction: column; -// width: 64rem; -// margin: 23.1rem auto auto auto; -// @media (max-width: 1023px) { -// margin: 19rem auto auto auto; -// } -// @media (max-width: 743px) { -// margin: 8rem auto auto auto; -// width: 34.3rem; -// } -// ` -// const LogoContainer = styled.div` -// display: flex; -// align-items: center; -// justify-content: space-between; -// margin-bottom: 4rem; -// @media (max-width: 743px) { -// width: 19.8rem; -// height: 6.6rem; -// margin-bottom: 2.4rem; -// } -// ` -// const LogoPanda = styled.div` -// img { -// width: 10.3rem; -// height: 10.3rem; -// margin-right: 2.2rem; -// display: flex; -// } -// @media (max-width: 743px) { -// img { -// width: 5.1rem; -// height: 5.1rem; -// margin-right: 1.1rem; -// display: flex; -// } -// } -// ` -// const LogoPandaText = styled.div` -// img { -// width: 26.6rem; -// height: 9rem; -// display: flex; -// align-items: center; -// } -// @media (max-width: 743px) { -// img { -// width: 13.3rem; -// height: 4.5rem; -// } -// } -// ` -// const ButtonWrapper = styled.div` -// margin-bottom: 2.4rem; -// width: 100%; -// ` -// const SignupButton = styled(Button)` -// padding: 1.2rem 28.5rem; -// width: max-content; -// @media (max-width: 743px) { -// padding: 1.2rem 13.6rem; -// } -// ` -// const SimpleLoginWrapper = styled.div` -// height: fit-content; -// width: 100%; -// display: flex; -// align-items: center; -// justify-content: space-between; -// background-color: #e6f2ff; -// padding: 1.6rem 2.3rem; -// margin-bottom: 2.4rem; -// ` -// const SimpleLogin = styled.div` -// ${(props) => textStyle(16, 500)(props)} -// color: ${theme.colors.SecondaryGray[800]}; -// ` -// const ImageWrapper = styled.div` -// height: 4.2rem; -// width: 10rem; -// display: flex; -// gap: 1.6rem; -// flex-direction: row; -// align-items: center; -// img { -// width: 4.2rem; -// height: 4.2rem; -// display: flex; -// } -// ` -// const FooterContainer = styled.div` -// display: flex; -// justify-content: space-between; -// ` -// const First = styled.div` -// ${(props) => textStyle(14, 500)(props)} -// color: ${theme.colors.SecondaryGray[800]}; -// ` -// const Register = styled.div` -// text-decoration: underline; -// color: ${theme.colors.PrimaryBlue[100]}; -// ` diff --git a/src/hooks/useInfiniteScroll.ts b/src/hooks/useInfiniteScroll.ts index 78d465a3..497d3d83 100644 --- a/src/hooks/useInfiniteScroll.ts +++ b/src/hooks/useInfiniteScroll.ts @@ -1,4 +1,4 @@ -import { useEffect, RefObject } from 'react' +import { useEffect, RefObject, useRef } from 'react' type UseInfiniteScrollProps = { hasMore: boolean @@ -13,17 +13,17 @@ export function useInfiniteScroll({ observerRef, scrollContainerRef, }: UseInfiniteScrollProps) { + const loadingRef = useRef(false) + useEffect(() => { if (!hasMore || !observerRef.current || !scrollContainerRef.current) return - let loading = false - const callback = async (entries: IntersectionObserverEntry[]) => { - if (entries[0].isIntersecting && !loading) { - loading = true + if (entries[0].isIntersecting && !loadingRef.current) { + loadingRef.current = true await loadMore() setTimeout(() => { - loading = false + loadingRef.current = false }, 500) } } From 4a14c3b4bb756071409111a7d6a07e748b36eed5 Mon Sep 17 00:00:00 2001 From: ramong26 Date: Thu, 22 May 2025 23:13:46 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feature:=EC=A0=84=EC=B2=B4=EC=A0=81?= =?UTF-8?q?=EC=9D=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/assets/svg/no_comment.svg | 11 ++ .../[productId]/ItemsDetailQuestionArrary.tsx | 4 +- src/app/addboard/addboard.module.scss | 56 ++++++++ src/app/addboard/page.tsx | 63 ++++++++ src/app/additem/page.tsx | 55 ------- src/app/boards/BoardDetailArrary.module.scss | 1 + src/app/boards/BoardDetailArrary.tsx | 9 +- src/app/boards/BoardList.tsx | 19 ++- .../boards/[id]/BoardCommentList.module.scss | 28 ++++ src/app/boards/[id]/BoardCommentList.tsx | 77 ++++++++++ .../boards/[id]/BoardDescription.module.scss | 63 ++++++++ src/app/boards/[id]/BoardDescription.tsx | 84 +++++++++++ src/app/boards/[id]/BoardId.module.scss | 14 ++ src/app/boards/[id]/page.tsx | 21 +++ src/components/common/Button.module.scss | 65 +++++++++ src/components/common/Button.tsx | 136 ++++-------------- .../common/ButtonImageInput.module.scss | 85 +++++++++++ src/components/common/ButtonImageInput.tsx | 122 +++------------- src/components/common/DropDown.module.scss | 89 ++++++++++++ src/components/common/DropDown.tsx | 128 ++++------------- src/components/common/Tag.module.scss | 17 +++ src/components/common/Tag.tsx | 28 +--- .../common/TextInputPlaceholder.module.scss | 26 ++++ .../common/TextInputPlaceholder.tsx | 60 ++++---- src/components/domain/Nav/ItemsNavVar.tsx | 4 +- src/hooks/useGetArticle.ts | 1 + src/lib/api/service/articleService.tsx | 4 +- src/lib/api/service/commentService.tsx | 3 +- 28 files changed, 844 insertions(+), 429 deletions(-) create mode 100644 public/assets/svg/no_comment.svg create mode 100644 src/app/addboard/addboard.module.scss create mode 100644 src/app/addboard/page.tsx create mode 100644 src/app/boards/[id]/BoardCommentList.module.scss create mode 100644 src/app/boards/[id]/BoardCommentList.tsx create mode 100644 src/app/boards/[id]/BoardDescription.module.scss create mode 100644 src/app/boards/[id]/BoardDescription.tsx create mode 100644 src/app/boards/[id]/BoardId.module.scss create mode 100644 src/app/boards/[id]/page.tsx create mode 100644 src/components/common/Button.module.scss create mode 100644 src/components/common/ButtonImageInput.module.scss create mode 100644 src/components/common/DropDown.module.scss create mode 100644 src/components/common/Tag.module.scss create mode 100644 src/components/common/TextInputPlaceholder.module.scss diff --git a/public/assets/svg/no_comment.svg b/public/assets/svg/no_comment.svg new file mode 100644 index 00000000..d17ed062 --- /dev/null +++ b/public/assets/svg/no_comment.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/app/Items/[productId]/ItemsDetailQuestionArrary.tsx b/src/app/Items/[productId]/ItemsDetailQuestionArrary.tsx index c8b98dbc..3f31d252 100644 --- a/src/app/Items/[productId]/ItemsDetailQuestionArrary.tsx +++ b/src/app/Items/[productId]/ItemsDetailQuestionArrary.tsx @@ -16,8 +16,8 @@ import styles from './ItemsDetailQuestionArrary.module.scss' interface ItemsDetailQuestionArraryProps { productQuestion: PostCommentType - setIsEditing: React.Dispatch> - isEditing: boolean + setIsEditing?: React.Dispatch> + isEditing?: boolean } const ItemsDetailQuestionArrary = ({ diff --git a/src/app/addboard/addboard.module.scss b/src/app/addboard/addboard.module.scss new file mode 100644 index 00000000..e309416b --- /dev/null +++ b/src/app/addboard/addboard.module.scss @@ -0,0 +1,56 @@ +@import '../../styles/variables.scss'; + +.bone { + width: 120rem; + display: flex; + align-items: center; + margin: 1.5rem auto 6.9rem auto; + flex-direction: column; + @media (max-width: 1023px) { + width: 69.6rem; + margin-bottom: 7.8rem; + } + @media (max-width: 743px) { + width: 34.6rem; + margin-bottom: 7rem; + } +} +.header { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + margin-bottom: 3.2rem; +} +.product-register { + @include apply-font($font-20, 700); + color: $secondaryGray-800; +} +.button-wrapper { + width: max-content; +} +.register-button { + padding: 0.8rem 2.3rem; + width: max-content; +} +.main { + width: 100%; +} +.display-wrapper { + width: 100%; + margin-bottom: 2.4rem; + @media (max-width: 743px) { + margin-bottom: 1.6rem; + } +} +.product-text { + @include apply-font($font-18, 700); + color: $secondaryGray-800; + margin-bottom: 1.6rem; +} +.tag-display { + display: flex; + gap: 0.75rem; + width: 100%; + flex-wrap: wrap; +} diff --git a/src/app/addboard/page.tsx b/src/app/addboard/page.tsx new file mode 100644 index 00000000..b416139a --- /dev/null +++ b/src/app/addboard/page.tsx @@ -0,0 +1,63 @@ +'use client' +import React, { useState } from 'react' + +import ItemsNavVar from '../../components/domain/Nav/ItemsNavVar' +import Button from '../../components/common/Button' +import ButtonImage from '../../components/common/ButtonImageInput' +import TextInputPlaceholder from '../../components/common/TextInputPlaceholder' + +import styles from './addboard.module.scss' + +const AddBoard = () => { + const [productTitle, setProductTitle] = useState('') + const [productDescription, setProductDescription] = useState('') + + const isState = productTitle.length >= 1 && productDescription.length >= 1 + + return ( + <> + +
+
+
게시글 쓰기
+
+ +
+
+
+
+
제목
+ setProductTitle(e.target.value)} + /> +
+
+
내용
+ setProductDescription(e.target.value)} + /> +
+ +
+
상품 이미지
+ +
+
+
+ + ) +} + +export default AddBoard diff --git a/src/app/additem/page.tsx b/src/app/additem/page.tsx index f7d6a5dd..604851c7 100644 --- a/src/app/additem/page.tsx +++ b/src/app/additem/page.tsx @@ -114,58 +114,3 @@ const AddItem = () => { } export default AddItem - -// const Bone = styled.div` -// width: 120rem; -// display: flex; -// align-items: center; -// margin: 1.5rem auto 6.9rem auto; -// flex-direction: column; -// @media (max-width: 1023px) { -// width: 69.6rem; -// margin-bottom: 7.8rem; -// } -// @media (max-width: 743px) { -// width: 34.6rem; -// margin-bottom: 7rem; -// } -// ` -// const Header = styled.div` -// display: flex; -// align-items: center; -// justify-content: space-between; -// width: 100%; -// margin-bottom: 2.4rem; -// ` -// const ProductRegister = styled.div` -// ${(props) => textStyle(20, 700)(props)} -// color: ${theme.colors.SecondaryGray[800]}; -// ` -// const ButtonWrapper = styled.div` -// width: max-content; -// ` -// const RegisterButton = styled(Button)` -// padding: 0.8rem 2.3rem; -// width: max-content; -// ` -// const Main = styled.div` -// width: 100%; -// ` -// const DisplayWrapper = styled.div` -// width: 100%; -// margin-bottom: 3.2rem; -// @media (max-width: 743px) { -// margin-bottom: 2.4rem; -// } -// ` -// const ProductText = styled.div` -// ${(props) => textStyle(18, 700)(props)} -// color: ${theme.colors.SecondaryGray[800]}; -// margin-bottom: 1.6rem; -// ` -// const TagDisplay = styled.div` -// display: flex; -// gap: 0.75rem; -// width: 100%; -// flex-wrap: wrap; -// ` diff --git a/src/app/boards/BoardDetailArrary.module.scss b/src/app/boards/BoardDetailArrary.module.scss index d148657d..9535f784 100644 --- a/src/app/boards/BoardDetailArrary.module.scss +++ b/src/app/boards/BoardDetailArrary.module.scss @@ -10,6 +10,7 @@ padding-bottom: 2.4rem; padding-right: 2rem; margin-top: 2.4rem; + cursor: pointer; @media (max-width: 743px) { margin-top: 0; margin-bottom: 2.4rem; diff --git a/src/app/boards/BoardDetailArrary.tsx b/src/app/boards/BoardDetailArrary.tsx index 6e10d79f..be0162ed 100644 --- a/src/app/boards/BoardDetailArrary.tsx +++ b/src/app/boards/BoardDetailArrary.tsx @@ -1,6 +1,6 @@ import React from 'react' import Image from 'next/image' - +import { useRouter } from 'next/navigation' import { GetArticleIdType } from '../../types/article' import { diffDate } from '../../utils/datetime' import { formatDate } from '../../utils/datetime' @@ -14,9 +14,14 @@ type BestBoardsProps = { } const BoardDetailArrary = ({ article }: BestBoardsProps) => { + console.log('BoardDetailArrary', article) + const router = useRouter() + const handleClickBoardID = () => { + router.push(`/boards/${article.id}`) + } return ( <> -
+
{article.content}
{article.image && ( diff --git a/src/app/boards/BoardList.tsx b/src/app/boards/BoardList.tsx index bf4db0fa..a773e4b1 100644 --- a/src/app/boards/BoardList.tsx +++ b/src/app/boards/BoardList.tsx @@ -31,7 +31,7 @@ const BoardList = () => { const [selectedOption, setSelectedOption] = useState('recent') const [searchTerm, setSearchTerm] = useState('') const debouncedSearchTerm = useDebounce(searchTerm, 500) - + const [isMobile, setIsMobile] = useState(false) const orderBy = selectedOption === 'recent' ? 'recent' : 'like' // 페이지네이션을 위한 상태 useEffect(() => { @@ -58,7 +58,16 @@ const BoardList = () => { observerRef, scrollContainerRef, }) + useEffect(() => { + const checkIsMobile = () => { + setIsMobile(window.innerWidth <= 743) + } + + checkIsMobile() + window.addEventListener('resize', checkIsMobile) + return () => window.removeEventListener('resize', checkIsMobile) + }, []) if (!articleList) { // 데이터가 없을 때 로딩 상태를 보여줄 수 있고, 데이터가 도착하면 다시 렌더링되어 실제 게시글 목록이 보임임 return null @@ -67,7 +76,7 @@ const BoardList = () => { <>
게시글
-
@@ -84,11 +93,11 @@ const BoardList = () => {
{ - setSelectedOption(value) - }} + onChange={(value) => setSelectedOption(value)} />
diff --git a/src/app/boards/[id]/BoardCommentList.module.scss b/src/app/boards/[id]/BoardCommentList.module.scss new file mode 100644 index 00000000..38d568a8 --- /dev/null +++ b/src/app/boards/[id]/BoardCommentList.module.scss @@ -0,0 +1,28 @@ +@import '../../../styles/variables.scss'; + +.title-comment-font { + @include apply-font($font-16, 600); + padding-bottom: 0.9rem; +} + +.register-button { + padding: 0.8rem 2.3rem; + width: max-content; +} +.register-button-container { + display: flex; + justify-content: end; + padding-top: 1.6rem; +} + +.inquiry-empty-wrapper { + display: flex; + align-items: center; + justify-content: center; + padding-top: 4rem; +} +.items-question-wrapper { + position: relative; + display: flex; + flex-direction: column; +} diff --git a/src/app/boards/[id]/BoardCommentList.tsx b/src/app/boards/[id]/BoardCommentList.tsx new file mode 100644 index 00000000..9f8a9afe --- /dev/null +++ b/src/app/boards/[id]/BoardCommentList.tsx @@ -0,0 +1,77 @@ +'use client' +import React, { useEffect } from 'react' +import { useState } from 'react' +import Image from 'next/image' + +import ItemsDetailQuestionArrary from '@/app/items/[productId]/ItemsDetailQuestionArrary' +import commentService from '../../../lib/api/service/commentService' +import TextInputPlaceholder from '@/components/common/TextInputPlaceholder' +import Button from '../../../components/common/Button' + +import NoComment from '../../../../public/assets/svg/no_comment.svg' + +import styles from './BoardCommentList.module.scss' +import { useParams } from 'next/navigation' +const BoardCommentList = () => { + const [articleComment, setArticleComment] = useState('') + const [productQuestion, setProductQuestion] = useState([]) + const { id } = useParams() + useEffect(() => { + if (!id) return + const fetchData = async () => { + try { + const response = await commentService.getArticleComment(id, 5) + setProductQuestion(response.data?.list ?? []) + console.log('BoardDescription', response.data) + } catch (error) { + console.error('Error fetching article:', error) + } + } + fetchData() + }, [id]) + + const isState = articleComment.length >= 1 + return ( +
+
+
댓글달기
+ setArticleComment(e.target.value)} + /> +
+ +
+
+
+ {!productQuestion || productQuestion.length === 0 ? ( +
+ 댓글이 없습니다 +
+ ) : ( + <> +
+ {productQuestion.map((question, index) => ( + + ))} +
+ + )} +
+
+ ) +} + +export default BoardCommentList diff --git a/src/app/boards/[id]/BoardDescription.module.scss b/src/app/boards/[id]/BoardDescription.module.scss new file mode 100644 index 00000000..7746a87b --- /dev/null +++ b/src/app/boards/[id]/BoardDescription.module.scss @@ -0,0 +1,63 @@ +@import '../../../styles/variables.scss'; + + +.article-wrapper { + display: flex; + flex-direction: column; + gap: 1.6rem; + padding-bottom: 1.6rem; + border-bottom: 1px solid $secondaryGray-200; +} +.article-title { + display: flex; + align-items: center; + justify-content: space-between; + @include apply-font($font-20, 700); +} +.article-content { + display: flex; + align-items: center; + justify-content: space-between; + width: 35.4rem; +} +.article-profile-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1.6rem; +} + +.article-profile-info { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.8rem; +} + +.article-profile-nickname { + @include apply-font($font-14, 500); +} + +.article-profile-date { + @include apply-font($font-14, 400); + color: $secondaryGray-400; +} + +.article-heart-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.4rem; + padding: 0.4rem 1.2rem; + border: 1px solid $secondaryGray-200; + border-radius: 3.5rem; +} + +.article-heart-image { + display: flex; +} + +.article-content-bottom { + @include apply-font($font-18, 400); + padding-top: 2.4rem; +} diff --git a/src/app/boards/[id]/BoardDescription.tsx b/src/app/boards/[id]/BoardDescription.tsx new file mode 100644 index 00000000..837f3d90 --- /dev/null +++ b/src/app/boards/[id]/BoardDescription.tsx @@ -0,0 +1,84 @@ +'use client' + +import { useParams } from 'next/navigation' +import { useEffect, useState } from 'react' + +import ArticleService from '../../../lib/api/service/articleService' +import { GetArticleIdType } from '../../../types/article' +import { diffDate } from '../../../utils/datetime' +import { formatDate } from '../../../utils/datetime' + +import Setting from '../../../../public/assets/svg/setting_icon.svg' +import HeartInactive from '../../../../public/assets/image/heart_inactive.png' +import ProfileIcon from '../../../../public/assets/svg/profile_icon.svg' + +import styles from './BoardDescription.module.scss' +import Image from 'next/image' +const BoardDescription = () => { + const [articleId, setArticleId] = useState() + const { id } = useParams() + + useEffect(() => { + if (!id) return + const fetchData = async () => { + try { + const response = await ArticleService.getArticleId(id) + setArticleId(response.data) + console.log('BoardDescription', response.data) + } catch (error) { + console.error('Error fetching article:', error) + } + } + fetchData() + }, [id]) + if (!articleId) return null + return ( +
+
+
+ {articleId?.title} + 설정 아이콘 +
+
+
+
+ 프로필 아이콘 +
+
+
+ {articleId?.writer.nickname} +
+
+ {diffDate(articleId.createdAt) > 31 ? ( + <> + {formatDate(articleId.createdAt)} + + ) : ( + <> + {diffDate(articleId.createdAt)} + 일 전 + + )} +
+
+
+
+
+
+ 하트 비활성화/활성화 +
+
+ {articleId?.likeCount} +
+
+
+
+
+
+ {articleId?.content} +
+
+ ) +} + +export default BoardDescription diff --git a/src/app/boards/[id]/BoardId.module.scss b/src/app/boards/[id]/BoardId.module.scss new file mode 100644 index 00000000..829cca58 --- /dev/null +++ b/src/app/boards/[id]/BoardId.module.scss @@ -0,0 +1,14 @@ +.bone { + width: 120rem; + height: 88.3rem; + margin: 3.2rem auto 9.5rem auto; + display: flex; + flex-direction: column; + gap: 3.2rem; +} +.question-wrapper { + display: flex; + align-items: center; + justify-content: center; + padding-top: 4rem; +} diff --git a/src/app/boards/[id]/page.tsx b/src/app/boards/[id]/page.tsx new file mode 100644 index 00000000..4c48bab5 --- /dev/null +++ b/src/app/boards/[id]/page.tsx @@ -0,0 +1,21 @@ +'use Client' + +import BoardDescription from './BoardDescription' +import BoardCommentList from './BoardCommentList' +import ItemsNavVar from '../../../components/domain/Nav/ItemsNavVar' +import styles from './BoardId.module.scss' + +const BoardId = () => { + return ( +
+ + +
+ + +
+
+ ) +} + +export default BoardId diff --git a/src/components/common/Button.module.scss b/src/components/common/Button.module.scss new file mode 100644 index 00000000..8bef815f --- /dev/null +++ b/src/components/common/Button.module.scss @@ -0,0 +1,65 @@ +@import '../../styles/variables.scss'; + +.button-wrapper { + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + border: none; + background: #007aff; + color: $white; + transition: all 0.3s ease-in-out; + + &:hover { + background: #005fcc; // PrimaryBlue[200] + transform: scale(1.01); + } + + &:active { + transform: scale(0.95); + } + + &:disabled { + background: #a0a0a0; // SecondaryGray[400] + cursor: not-allowed; + } +} + +.button-inner { + display: flex; + align-items: center; + gap: 0.3rem; +} + +.button-inner-text { + display: flex; + align-items: center; + justify-content: center; +} + +/* 사이즈별 클래스 */ +.size56 { + font-size: 20px; + font-weight: 600; + border-radius: 40px; +} +.size48 { + font-size: 18px; + font-weight: 600; + border-radius: 40px; +} +.size48_5 { + font-size: 16px; + font-weight: 600; + border-radius: 8px; +} +.size42_5 { + font-size: 16px; + font-weight: 600; + border-radius: 8px; +} +.size0 { + font-size: 16px; + font-weight: 600; + border-radius: 40px; +} diff --git a/src/components/common/Button.tsx b/src/components/common/Button.tsx index 8439db93..099bd1aa 100644 --- a/src/components/common/Button.tsx +++ b/src/components/common/Button.tsx @@ -1,6 +1,6 @@ -import styled from 'styled-components' import Link from 'next/link' import { ReactNode, CSSProperties } from 'react' +import styles from './Button.module.scss' interface ButtonProps { size: number @@ -10,7 +10,6 @@ interface ButtonProps { style?: CSSProperties prefix?: ReactNode suffix?: ReactNode - as?: 'button' | typeof Link to?: string className?: string } @@ -28,124 +27,47 @@ const Button = ({ }: ButtonProps) => { const isLink = !!to - if (isLink && !to) { - console.error('Error: "to" prop is required when "as" is Link.') - return null + const sizeClassMap: Record = { + 56: styles.size56, + 48: styles.size48, + 48.5: styles.size48_5, + 42.5: styles.size42_5, } + + const sizeClass = sizeClassMap[size] || styles.size48_5 + + const buttonClass = `${styles['button-wrapper']} ${sizeClass} ${ + className ?? '' + }` + + const content = ( +
+ {prefix && {prefix}} + {children} + {suffix && {suffix}} +
+ ) + if (isLink) { return ( - - - {prefix && {prefix}} - {children} - {suffix && {suffix}} - - +
+ {content} +
) } + return ( - - - {prefix && {prefix}} - {children} - {suffix && {suffix}} - - + {content} + ) } export default Button - -interface StyledButtonWrapper { - size?: number -} - -const ButtonWrapper = styled.button` - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - border: none; - background: ${({ theme }) => theme.colors.PrimaryBlue[100]}; - color: ${({ theme }) => theme.colors.SecondaryGray[50]}; - font-weight: ${({ size }) => ButtonSize[size!]?.fontWeight || 600}; - border-radius: ${({ size }) => ButtonSize[size!]?.borderRadius || '40px'}; - - font-size: ${({ size }) => ButtonSize[size!]?.fontSize || '16px'}; - - transition: all 0.3s ease-in-out; - &:hover { - background: ${({ theme }) => theme.colors.PrimaryBlue[200]}; - transform: scale(1.01); - } - &:active { - transform: scale(0.95); - } - &:disabled { - background: ${({ theme }) => theme.colors.SecondaryGray[400]}; - cursor: not-allowed; - } -` -const ButtonInner = styled.span` - display: flex; - align-items: center; - gap: 0.3rem; -` -const ButtonInnerText = styled.span` - display: flex; - align-items: center; - justify-content: center; -` - -//Record는 TypeScript의 내장 제네릭 유틸리티 타입: 객체의 키와 값의 타입을 명확하게 정의 -// theme.d.ts와 다른 이유는 DefaultTheme 타입을 확장했고 여기는 안 함 -type ButtonSizeType = Record< - number, - { - fontSize: string - fontWeight: number - - borderRadius: string - } -> -const ButtonSize: ButtonSizeType = { - 56: { - fontSize: '20px', - fontWeight: 600, - - borderRadius: '40px', - }, - 48: { - fontSize: '18px', - fontWeight: 600, - - borderRadius: '40px', - }, - - 42.5: { - fontSize: '16px', - fontWeight: 600, - - borderRadius: '8px', - }, - 48.5: { - fontSize: '16px', - fontWeight: 600, - - borderRadius: '8px', - }, - 0: { - fontSize: '16px', - fontWeight: 600, - borderRadius: '40px', - }, -} diff --git a/src/components/common/ButtonImageInput.module.scss b/src/components/common/ButtonImageInput.module.scss new file mode 100644 index 00000000..e6b2bd9f --- /dev/null +++ b/src/components/common/ButtonImageInput.module.scss @@ -0,0 +1,85 @@ +@import '../../styles/variables.scss'; + +.image-wrapper { + width: 100%; + height: auto; + display: flex; + gap: 24px; + @media (max-width: 1023px) { + gap: 10px; + } + @media (max-width: 743px) { + width: 34.6rem; + height: 16.8rem; + position: relative; + } +} +.bone { + width: 28.2rem; + height: 28.2rem; + background-color: #e5e7eb; + border-radius: 12px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + position: relative; + @media (max-width: 1023px) { + width: 16.8rem; + height: 16.8rem; + } +} +.container { + width: fit-content; + height: 8.6rem; + display: flex; + align-items: center; + justify-content: space-between; + flex-direction: column; +} +.image-input { + display: none; +} +.text { + @include apply-font($font-16, 400); + color: $secondaryGray-400; +} +.preview-image-wrapper { + width: fit-content; + height: fit-content; + border-radius: 12px; + + img { + width: 28.2rem; + height: 28.2rem; + border-radius: 12px; + } + @media (max-width: 1023px) { + img { + width: 16.8rem; + height: 16.8rem; + } + } +} + +.delete-icon { + img { + width: 2rem; + height: 2rem; + position: relative; + right: -25.25rem; + top: -27.25rem; + cursor: pointer; + } + @media (max-width: 1023px) { + img { + right: -14.25rem; + top: -16.25rem; + } + } +} +.error-message { + @include apply-font($font-16, 400); + color: $error; + margin-top: 1rem; +} diff --git a/src/components/common/ButtonImageInput.tsx b/src/components/common/ButtonImageInput.tsx index c76dbc45..d12bcc73 100644 --- a/src/components/common/ButtonImageInput.tsx +++ b/src/components/common/ButtonImageInput.tsx @@ -1,14 +1,12 @@ import React, { useRef, useState } from 'react' +import Image from 'next/image' import Plus from '../../../public/assets/svg/plus_icon.svg' import Delete from '../../../public/assets/svg/delete_tag.svg' -import styled from 'styled-components' -import { theme } from '../../styles/theme' -import { textStyle } from '../../styles/textStyle' -import Image from 'next/image' +import styles from './ButtonImageInput.module.scss' -const ButtonImage = () => { +const ButtonImageInput = () => { const fileInputRef = useRef(null) const [imagePreview, setImagePreview] = useState('') const [errorMeassage, setErrorMeassage] = useState('') @@ -39,26 +37,28 @@ const ButtonImage = () => { console.log(imagePreview) return ( <> - - - +
+
+
이미지등록아이콘 - 이미지 등록 - - +
이미지 등록
+
+
{imagePreview && (
- +
미리보기 이미지 - - +
+
삭제버튼 - +
)} - - {errorMeassage && {errorMeassage}} - + {errorMeassage && ( +
{errorMeassage}
+ )} + { ) } -export default ButtonImage - -const ImageWrapper = styled.div` - width: 100%; - height: auto; - display: flex; - gap: 24px; - @media (max-width: 1023px) { - gap: 10px; - } - @media (max-width: 743px) { - width: 34.6rem; - height: 16.8rem; - position: relative; - } -` -const Bone = styled.div` - width: 28.2rem; - height: 28.2rem; - background-color: #e5e7eb; - border-radius: 12px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - position: relative; - @media (max-width: 1023px) { - width: 16.8rem; - height: 16.8rem; - } -` -const Container = styled.div` - width: fit-content; - height: 8.6rem; - display: flex; - align-items: center; - justify-content: space-between; - flex-direction: column; -` -const ImageInput = styled.input` - display: none; -` -const Text = styled.div` - ${(props) => textStyle(16, 400)(props)} - color: ${theme.colors.SecondaryGray[400]}; -` -const PreviewImageWrapper = styled.div` - width: fit-content; - height: fit-content; - border-radius: 12px; - - img { - width: 28.2rem; - height: 28.2rem; - border-radius: 12px; - } - @media (max-width: 1023px) { - img { - width: 16.8rem; - height: 16.8rem; - } - } -` - -const DeleteIcon = styled.div` - img { - width: 2rem; - height: 2rem; - position: relative; - right: -25.25rem; - top: -27.25rem; - cursor: pointer; - } - @media (max-width: 1023px) { - img { - right: -14.25rem; - top: -16.25rem; - } - } -` -const ErrorMessage = styled.div` - ${(props) => textStyle(16, 400)(props)} - color: ${theme.colors.error}; - margin-top: 1rem; -` +export default ButtonImageInput diff --git a/src/components/common/DropDown.module.scss b/src/components/common/DropDown.module.scss new file mode 100644 index 00000000..2038136a --- /dev/null +++ b/src/components/common/DropDown.module.scss @@ -0,0 +1,89 @@ +@import '../../styles/variables.scss'; + +.dropdown-container { + position: relative; +} + +.select-box { + width: 13rem; + height: 100%; + cursor: pointer; + padding: 0.8rem 2rem; + border: 1px solid #ccc; + border-radius: 1.2rem; + background-color: white; + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + + @media (max-width: 743px) { + width: max-content; + height: max-content; + justify-content: center; + padding: 0.9rem; + position: absolute; + } +} + +.arrow-icon { + width: 1.5rem; + height: 1.5rem; + cursor: pointer; + transition: transform 0.3s ease; + + &.open { + transform: rotate(180deg); + } + + @media (max-width: 743px) { + width: 0; + height: 0; + } +} + +.select-option { + position: absolute; + top: 5.8rem; + width: 100%; + border: 1px solid #cccccc; + border-radius: 12px; + background-color: #ffffff; + color: #181818; + z-index: 10; + list-style: none; + display: flex; + align-items: center; + flex-direction: column; + justify-content: space-around; + + @include apply-font($font-16, 400); + + @media (max-width: 743px) { + top: 62px; + left: -72px; + z-index: 1; + width: 130px; + height: 84px; + } +} + +.option { + padding: 9px 28px; + cursor: pointer; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + border-radius: 12px; + + @include apply-font($font-16, 400); + + &:hover { + background-color: #f6f6f6; + } + + @media (max-width: 743px) { + padding: 7px 35px; + } +} diff --git a/src/components/common/DropDown.tsx b/src/components/common/DropDown.tsx index 6daf21c3..0c6a4eb3 100644 --- a/src/components/common/DropDown.tsx +++ b/src/components/common/DropDown.tsx @@ -1,13 +1,11 @@ 'use client' import React, { useRef, useState, useEffect } from 'react' -import styled from 'styled-components' import Image from 'next/image' import ArrowDown from '../../../public/assets/image/arrow_down.png' import Sort from '../../../public/assets/svg/sort_arrow.svg' -import { theme } from '../../styles/theme' -import { textStyle } from '../../styles/textStyle' +import styles from './DropDown.module.scss' type OptionType = { name: string @@ -30,6 +28,7 @@ const DropDown = ({ top = '0', }: DropDownProps) => { const [isOpen, setIsOpen] = useState(false) + const [isMobile, setIsMobile] = useState(false) const selectRef = useRef(null) const toggleDropdown = () => { @@ -42,119 +41,50 @@ const DropDown = ({ } useEffect(() => { - const handleResize = () => {} + const checkIsMobile = () => { + setIsMobile(window.innerWidth <= 743) + } - window.addEventListener('resize', handleResize) // resize: 창 크기가 변경될 때 발생하는 이벤트 / handleResize: 이벤트가 발생할 때 실행할 함수 - return () => window.removeEventListener('resize', handleResize) - }, []) + checkIsMobile() // 초기 실행 + window.addEventListener('resize', checkIsMobile) + return () => window.removeEventListener('resize', checkIsMobile) + }, []) return ( -
- +
- {window.innerWidth <= 743 ? ( + {isMobile ? ( Sort ) : ( selectList.find((item) => item.value === selected)?.name )} - - + Arrow +
+ {isOpen && ( - +
    {selectList.map((item) => ( -
)}
) } export default DropDown - -const SelectBox = styled.div<{ $left?: string; $top?: string }>` - width: 13rem; - height: 100%; - cursor: pointer; - padding: 0.8rem 2rem; - border: 1px solid ${theme.colors.SecondaryGray[200]}; - border-radius: 1.2rem; - background-color: white; - position: relative; - display: flex; - align-items: center; - justify-content: space-between; - @media (max-width: 743px) { - width: max-content; - height: max-content; - justify-content: center; - - left: ${({ $left }) => $left || '0'}; - top: ${({ $top }) => $top || '0'}; - padding: 0.9rem; - } -` -const ArrowDownImage = styled.img.withConfig({ - shouldForwardProp: (prop) => prop !== 'isOpen', -})<{ isOpen: boolean }>` - width: 1.5rem; - height: 1.5rem; - cursor: pointer; - transition: transform 0.3s ease; - transform: ${({ isOpen }) => (isOpen ? 'rotate(180deg)' : 'rotate(0deg)')}; - - @media (max-width: 743px) { - width: 0; - height: 0; - } -` -const SelectOption = styled.ul` - position: absolute; - top: 5.8rem; - - width: 100%; - border: 1px solid #cccccc; - border-radius: 12px; - background-color: #ffffff; - color: #181818; - ${(props) => textStyle(16, 400)(props)} - - z-index: 10; - list-style: none; - - display: flex; - align-items: center; - flex-direction: column; - - justify-content: space-around; - @media (max-width: 743px) { - position: absolute; - top: 62px; - left: -72px; - z-index: 1; - width: 130px; - height: 84px; - } -` -const Option = styled.li` - ${(props) => textStyle(16, 400)(props)} - padding: 9px 28px; - cursor: pointer; - width: inherit; - display: flex; - align-items: center; - justify-content: center; - border-radius: 12px; - &:hover { - background-color: #f6f6f6; - } - @media (max-width: 743px) { - padding: 7px 35px; - } -` diff --git a/src/components/common/Tag.module.scss b/src/components/common/Tag.module.scss new file mode 100644 index 00000000..fd2e0366 --- /dev/null +++ b/src/components/common/Tag.module.scss @@ -0,0 +1,17 @@ +@import '../../styles/variables.scss'; + +.bone { + width: fit-content; + height: fit-content; + display: flex; + align-items: center; + justify-content: center; + padding: 5px 12px 5px 16px; + background-color: $secondaryGray-100; + border-radius: 26px; +} +.text { + @include apply-font($font-16, 400); + color: $secondaryGray-800; + margin-right: 0.5rem; +} diff --git a/src/components/common/Tag.tsx b/src/components/common/Tag.tsx index 8c2e43c5..88d19e0c 100644 --- a/src/components/common/Tag.tsx +++ b/src/components/common/Tag.tsx @@ -1,11 +1,9 @@ import React from 'react' +import Image from 'next/image' import Delete from '../../../public/assets/svg/delete_tag.svg' -import styled from 'styled-components' -import { theme } from '../../styles/theme' -import { textStyle } from '../../styles/textStyle' -import Image from 'next/image' +import styles from './Tag.module.scss' interface TagProps { tag: string @@ -17,30 +15,14 @@ const Tag = ({ tag, onClick, showDelete = false }: TagProps) => { console.log('Tag 컴포넌트에 전달된 productTags:', tag) return (
- - #{tag} +
+
#{tag}
{showDelete && ( 삭제 onClick?.(tag)} /> )} - +
) } export default Tag - -const Bone = styled.div` - width: fit-content; - height: fit-content; - display: flex; - align-items: center; - justify-content: center; - padding: 5px 12px 5px 16px; - background-color: ${theme.colors.SecondaryGray[100]}; - border-radius: 26px; -` -const Text = styled.div` - ${(props) => textStyle(16, 400)(props)} - color: ${theme.colors.SecondaryGray[800]}; - margin-right: 0.5rem; -` diff --git a/src/components/common/TextInputPlaceholder.module.scss b/src/components/common/TextInputPlaceholder.module.scss new file mode 100644 index 00000000..b7e0fe21 --- /dev/null +++ b/src/components/common/TextInputPlaceholder.module.scss @@ -0,0 +1,26 @@ +@import '../../styles/variables.scss'; + +.bone { + width: 100%; + + background-color: $secondaryGray-100; + @include apply-font($font-16, 400); + color: $secondaryGray-800; + border-radius: 12px; + border: none; + resize: none; + box-sizing: border-box; + display: block; + cursor: text; + word-break: break-word; + overflow-wrap: break-word; + white-space: pre-wrap; + + &::placeholder { + color: $secondaryGray-400; + } + + @media (max-width: 743px) { + @include apply-font($font-14, 400); + } +} diff --git a/src/components/common/TextInputPlaceholder.tsx b/src/components/common/TextInputPlaceholder.tsx index 8b5418b0..2a6579c9 100644 --- a/src/components/common/TextInputPlaceholder.tsx +++ b/src/components/common/TextInputPlaceholder.tsx @@ -1,7 +1,6 @@ import React from 'react' -import styled from 'styled-components' -import { theme } from '../../styles/theme' -import { textStyle } from '../../styles/textStyle' + +import styles from './TextInputPlaceholder.module.scss' interface TextInputPlaceholderProps { placeholder?: string @@ -21,10 +20,13 @@ const TextInputPlaceholder = ({ onKeyDown, }: TextInputPlaceholderProps) => { return ( - ` - width: 100%; - height: ${(props) => props.$height ?? '56px'}; - padding: ${(props) => props.$padding ?? '15px 24px'}; - background-color: ${theme.colors.SecondaryGray[100]}; - ${(props) => textStyle(16, 400)(props)} - color: ${theme.colors.SecondaryGray[800]}; - border-radius: 12px; - border: none; - resize: none; - box-sizing: border-box; - display: block; - cursor: text; - word-break: break-word; - overflow-wrap: break-word; - white-space: pre-wrap; +// const Bone = styled.textarea<{ $height?: string; $padding?: string }>` +// width: 100%; +// height: ${(props) => props.$height ?? '56px'}; +// padding: ${(props) => props.$padding ?? '15px 24px'}; +// background-color: ${theme.colors.SecondaryGray[100]}; +// ${(props) => textStyle(16, 400)(props)} +// color: ${theme.colors.SecondaryGray[800]}; +// border-radius: 12px; +// border: none; +// resize: none; +// box-sizing: border-box; +// display: block; +// cursor: text; +// word-break: break-word; +// overflow-wrap: break-word; +// white-space: pre-wrap; - ::placeholder { - color: ${theme.colors.SecondaryGray[400]}; - } +// ::placeholder { +// color: ${theme.colors.SecondaryGray[400]}; +// } - @media (max-width: 743px) { - ${(props) => textStyle(14, 400)(props)} - } -` +// @media (max-width: 743px) { +// ${(props) => textStyle(14, 400)(props)} +// } +// ` diff --git a/src/components/domain/Nav/ItemsNavVar.tsx b/src/components/domain/Nav/ItemsNavVar.tsx index 6a63fdef..e447af8e 100644 --- a/src/components/domain/Nav/ItemsNavVar.tsx +++ b/src/components/domain/Nav/ItemsNavVar.tsx @@ -1,5 +1,8 @@ +'use client' + import Link from 'next/link' import React from 'react' +import Image from 'next/image' import LogoFace from '../../../../public/assets/image/logo_face.png' import Logo from '../../../../public/assets/image/logo_text.png' @@ -8,7 +11,6 @@ import ProfileIcon from '../../../../public/assets/svg/profile_icon.svg' import styled from 'styled-components' import { theme } from '../../../styles/theme' import { textStyle } from '../../../styles/textStyle' -import Image from 'next/image' interface ItemsNavVarProps { isItemsPage: boolean diff --git a/src/hooks/useGetArticle.ts b/src/hooks/useGetArticle.ts index 32da322a..89413342 100644 --- a/src/hooks/useGetArticle.ts +++ b/src/hooks/useGetArticle.ts @@ -1,3 +1,4 @@ +'use client' import { useEffect, useState } from 'react' import articleService from '../lib/api/service/articleService' import { GetArticleType } from '../types/article' diff --git a/src/lib/api/service/articleService.tsx b/src/lib/api/service/articleService.tsx index a021dcc1..d5e72c2a 100644 --- a/src/lib/api/service/articleService.tsx +++ b/src/lib/api/service/articleService.tsx @@ -33,10 +33,10 @@ class ArticleService { } getArticleId(articleId: number): Promise> { - return requestor.get(`p/articles/${articleId}`) + return requestor.get(`/api/proxy/articles/${articleId}`) } - patchArticleId(articleId, body) { + patchArticleId(articleId: number, body: GetArticleIdType) { // 틀릴수도 return requestor.patch(`/api/proxy/articles/${articleId}`, { data: body, diff --git a/src/lib/api/service/commentService.tsx b/src/lib/api/service/commentService.tsx index 30c25fdc..b331cf7c 100644 --- a/src/lib/api/service/commentService.tsx +++ b/src/lib/api/service/commentService.tsx @@ -46,8 +46,9 @@ class CommentService { } getArticleComment(articleId: number, limit: number, cursor?: number) { + const cursorParam = cursor !== undefined ? `&cursor=${cursor}` : '' return requestor.get( - `/api/proxy/articles/${articleId}/comments?limit=${limit}&cursor=${cursor}` + `/api/proxy/articles/${articleId}/comments?limit=${limit}${cursorParam}` ) } From 9a9b0f06a05c8c4876d0d3bfda075fb38d806818 Mon Sep 17 00:00:00 2001 From: ramong26 Date: Fri, 23 May 2025 10:45:22 +0900 Subject: [PATCH 4/5] =?UTF-8?q?style:=20=EB=AA=A8=EB=B0=94=EC=9D=BC=20?= =?UTF-8?q?=EB=B0=98=EC=9D=91=ED=98=95=EA=B9=8C=EC=A7=80=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[productId]/ItemsDetailDescription.tsx | 2 +- .../ItemsDetailQuestionTextarea.tsx | 2 +- src/app/Items/items.module.scss | 4 +-- src/app/Items/page.tsx | 14 ++++++++-- src/app/addboard/page.tsx | 2 ++ src/app/additem/page.tsx | 6 +++++ .../boards/[id]/BoardCommentList.module.scss | 27 +++++++++++++++++++ src/app/boards/[id]/BoardCommentList.tsx | 17 +++++++++--- .../boards/[id]/BoardDescription.module.scss | 18 ++++++++----- src/app/boards/[id]/BoardDescription.tsx | 2 +- src/app/boards/[id]/BoardId.module.scss | 14 +++++----- src/components/common/ButtonImageInput.tsx | 1 + src/{app => }/hooks/useCommentService.tsx | 4 +-- src/{app => }/hooks/useProductService.tsx | 4 +-- 14 files changed, 91 insertions(+), 26 deletions(-) rename src/{app => }/hooks/useCommentService.tsx (85%) rename src/{app => }/hooks/useProductService.tsx (81%) diff --git a/src/app/Items/[productId]/ItemsDetailDescription.tsx b/src/app/Items/[productId]/ItemsDetailDescription.tsx index 4eaca071..ca35d8fc 100644 --- a/src/app/Items/[productId]/ItemsDetailDescription.tsx +++ b/src/app/Items/[productId]/ItemsDetailDescription.tsx @@ -4,7 +4,7 @@ import React from 'react' import { useParams } from 'next/navigation' import Image from 'next/image' -import { useGetProductId } from '../../hooks/useProductService' +import { useGetProductId } from '../../../hooks/useProductService' import Tag from '../../../components/common/Tag' import { formatDate } from '../../../utils/datetime' diff --git a/src/app/Items/[productId]/ItemsDetailQuestionTextarea.tsx b/src/app/Items/[productId]/ItemsDetailQuestionTextarea.tsx index b43e1087..549ba2e0 100644 --- a/src/app/Items/[productId]/ItemsDetailQuestionTextarea.tsx +++ b/src/app/Items/[productId]/ItemsDetailQuestionTextarea.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useState } from 'react' import { useParams } from 'next/navigation' import Image from 'next/image' -import { useGetCommentService } from '../../hooks/useCommentService' +import { useGetCommentService } from '../../../hooks/useCommentService' import ItemsDetailQuestionArrary from './ItemsDetailQuestionArrary' import TextInputPlaceholder from '../../../components/common/TextInputPlaceholder' import Button from '../../../components/common/Button' diff --git a/src/app/Items/items.module.scss b/src/app/Items/items.module.scss index e339e6fe..471f8ce0 100644 --- a/src/app/Items/items.module.scss +++ b/src/app/Items/items.module.scss @@ -48,7 +48,7 @@ z-index: 10; display: flex; @media (max-width: 1023px) { - right: 412px; + right: 494px; } @media (max-width: 743px) { right: 308px; @@ -70,7 +70,7 @@ border: none; margin-right: 1.2rem; @media (max-width: 1023px) { - width: 15.125rem; + width: 24.2rem; padding: 9px 24px 9px 44px; } @media (max-width: 743px) { diff --git a/src/app/Items/page.tsx b/src/app/Items/page.tsx index b3be3720..ea2d758f 100644 --- a/src/app/Items/page.tsx +++ b/src/app/Items/page.tsx @@ -36,6 +36,7 @@ const Items = () => { const [selectedOption, setSelectedOption] = useState(selectList[0].value) const itemsPerPage = 10 // 페이지 네이션 const [currentPage, setCurrentPage] = useState(1) + const [isMobile, setIsMobile] = useState(false) useEffect(() => { const queryParams = new URLSearchParams() @@ -122,7 +123,16 @@ const Items = () => { return prevPage }) } + useEffect(() => { + const checkIsMobile = () => { + setIsMobile(window.innerWidth <= 743) + } + checkIsMobile() + window.addEventListener('resize', checkIsMobile) + + return () => window.removeEventListener('resize', checkIsMobile) + }, []) return ( <> @@ -152,8 +162,8 @@ const Items = () => {
{ diff --git a/src/app/addboard/page.tsx b/src/app/addboard/page.tsx index b416139a..595f7bde 100644 --- a/src/app/addboard/page.tsx +++ b/src/app/addboard/page.tsx @@ -35,6 +35,8 @@ const AddBoard = () => {
제목
setProductTitle(e.target.value)} /> diff --git a/src/app/additem/page.tsx b/src/app/additem/page.tsx index 604851c7..f8a2235c 100644 --- a/src/app/additem/page.tsx +++ b/src/app/additem/page.tsx @@ -66,6 +66,8 @@ const AddItem = () => {
상품명
setProductName(e.target.value)} /> @@ -84,6 +86,8 @@ const AddItem = () => {
판매 가격
setProductPrice(e.target.value)} /> @@ -92,6 +96,8 @@ const AddItem = () => {
태그
setTagInput(e.target.value)} onKeyDown={handleEnterDown} diff --git a/src/app/boards/[id]/BoardCommentList.module.scss b/src/app/boards/[id]/BoardCommentList.module.scss index 38d568a8..07b21ee2 100644 --- a/src/app/boards/[id]/BoardCommentList.module.scss +++ b/src/app/boards/[id]/BoardCommentList.module.scss @@ -20,9 +20,36 @@ align-items: center; justify-content: center; padding-top: 4rem; + @media (max-width: 1023px) { + padding-top: 3.2rem; + } } .items-question-wrapper { position: relative; display: flex; flex-direction: column; + @media (max-width: 1023px) { + padding-top: 3.2rem; + } + @media (max-width: 743px) { + padding-top: 2.4rem; + } +} +.button-wrapper { + display: flex; + justify-content: center; + margin: 4.6875rem auto; + width: max-content; + @include apply-font($font-18, 600); + color: $secondaryGray-100; + @media (max-width: 1023px) { + margin: 3.5rem auto 10.4375rem auto; + } + @media (max-width: 743px) { + margin: 4rem auto 2.5rem auto; + } +} +.list-button { + padding: 1.1rem 3.9rem; + width: max-content; } diff --git a/src/app/boards/[id]/BoardCommentList.tsx b/src/app/boards/[id]/BoardCommentList.tsx index 9f8a9afe..a8b09e1b 100644 --- a/src/app/boards/[id]/BoardCommentList.tsx +++ b/src/app/boards/[id]/BoardCommentList.tsx @@ -1,17 +1,18 @@ 'use client' -import React, { useEffect } from 'react' -import { useState } from 'react' +import React, { useEffect, useState } from 'react' import Image from 'next/image' +import { useParams } from 'next/navigation' import ItemsDetailQuestionArrary from '@/app/items/[productId]/ItemsDetailQuestionArrary' import commentService from '../../../lib/api/service/commentService' import TextInputPlaceholder from '@/components/common/TextInputPlaceholder' import Button from '../../../components/common/Button' +import BackIcon from '../../../../public/assets/svg/back_icon.svg' import NoComment from '../../../../public/assets/svg/no_comment.svg' import styles from './BoardCommentList.module.scss' -import { useParams } from 'next/navigation' + const BoardCommentList = () => { const [articleComment, setArticleComment] = useState('') const [productQuestion, setProductQuestion] = useState([]) @@ -69,6 +70,16 @@ const BoardCommentList = () => {
)} +
+ +
) diff --git a/src/app/boards/[id]/BoardDescription.module.scss b/src/app/boards/[id]/BoardDescription.module.scss index 7746a87b..03f4503a 100644 --- a/src/app/boards/[id]/BoardDescription.module.scss +++ b/src/app/boards/[id]/BoardDescription.module.scss @@ -1,6 +1,5 @@ @import '../../../styles/variables.scss'; - .article-wrapper { display: flex; flex-direction: column; @@ -10,23 +9,25 @@ } .article-title { display: flex; - align-items: center; + align-items: flex-start; justify-content: space-between; @include apply-font($font-20, 700); } .article-content { display: flex; align-items: center; - justify-content: space-between; - width: 35.4rem; } .article-profile-wrapper { display: flex; align-items: center; justify-content: space-between; gap: 1.6rem; + padding-right: 3.2rem; + border-right: 1px solid $secondaryGray-200; +} +.article-profile-image { + display: flex; } - .article-profile-info { display: flex; align-items: center; @@ -42,7 +43,9 @@ @include apply-font($font-14, 400); color: $secondaryGray-400; } - +.article-heart-main-wrapper { + padding-left: 3.2rem; +} .article-heart-wrapper { display: flex; align-items: center; @@ -60,4 +63,7 @@ .article-content-bottom { @include apply-font($font-18, 400); padding-top: 2.4rem; + @media (max-width: 1023px) { + padding-top: 1.6rem; + } } diff --git a/src/app/boards/[id]/BoardDescription.tsx b/src/app/boards/[id]/BoardDescription.tsx index 837f3d90..9c1fdf82 100644 --- a/src/app/boards/[id]/BoardDescription.tsx +++ b/src/app/boards/[id]/BoardDescription.tsx @@ -62,7 +62,7 @@ const BoardDescription = () => { -
+
하트 비활성화/활성화 diff --git a/src/app/boards/[id]/BoardId.module.scss b/src/app/boards/[id]/BoardId.module.scss index 829cca58..a94b2055 100644 --- a/src/app/boards/[id]/BoardId.module.scss +++ b/src/app/boards/[id]/BoardId.module.scss @@ -5,10 +5,12 @@ display: flex; flex-direction: column; gap: 3.2rem; -} -.question-wrapper { - display: flex; - align-items: center; - justify-content: center; - padding-top: 4rem; + @media (max-width: 1023px) { + width: 69.6rem; + gap: 4rem; + } + @media (max-width: 743px) { + width: 34.3rem; + gap: 3.2rem; + } } diff --git a/src/components/common/ButtonImageInput.tsx b/src/components/common/ButtonImageInput.tsx index d12bcc73..1b03f034 100644 --- a/src/components/common/ButtonImageInput.tsx +++ b/src/components/common/ButtonImageInput.tsx @@ -59,6 +59,7 @@ const ButtonImageInput = () => {
{errorMeassage}
)} { const [productQuestion, setProductQuestion] = useState({ diff --git a/src/app/hooks/useProductService.tsx b/src/hooks/useProductService.tsx similarity index 81% rename from src/app/hooks/useProductService.tsx rename to src/hooks/useProductService.tsx index 48afc7c1..9f0cdc74 100644 --- a/src/app/hooks/useProductService.tsx +++ b/src/hooks/useProductService.tsx @@ -2,8 +2,8 @@ import { useEffect, useState } from 'react' -import productService from '../../lib/api/service/productService' -import { GetProductIdTypes } from '../../types/product' +import productService from '../lib/api/service/productService' +import { GetProductIdTypes } from '../types/product' export const useGetProductId = (productId: number) => { const [productsId, setProductsId] = useState() From 840f0f9817b55e41f99951af61e3257927a4aff6 Mon Sep 17 00:00:00 2001 From: ramong26 Date: Fri, 23 May 2025 11:09:57 +0900 Subject: [PATCH 5/5] =?UTF-8?q?=EC=8B=AC=ED=99=94=EB=B9=BC=EA=B3=A0=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 32 ++++++++++++++++++++++++++++- package.json | 3 ++- src/lib/api/client/interceptors.tsx | 20 ++++++++---------- src/lib/stores/useAuthStore.ts | 16 +++++++++++++++ 4 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 src/lib/stores/useAuthStore.ts diff --git a/package-lock.json b/package-lock.json index 30aa0503..25860c78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,8 @@ "react-dom": "^19.0.0", "react-router-dom": "^7.5.0", "sass": "^1.89.0", - "styled-components": "^6.1.18" + "styled-components": "^6.1.18", + "zustand": "^5.0.5" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -6580,6 +6581,35 @@ "peerDependencies": { "zod": "^3.24.1" } + }, + "node_modules/zustand": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.5.tgz", + "integrity": "sha512-mILtRfKW9xM47hqxGIxCv12gXusoY/xTSHBYApXozR0HmQv299whhBeeAcRy+KrPPybzosvJBCOmVjq6x12fCg==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 7d924603..7a91c4f6 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "react-dom": "^19.0.0", "react-router-dom": "^7.5.0", "sass": "^1.89.0", - "styled-components": "^6.1.18" + "styled-components": "^6.1.18", + "zustand": "^5.0.5" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/src/lib/api/client/interceptors.tsx b/src/lib/api/client/interceptors.tsx index 283f37cf..a01d4bc7 100644 --- a/src/lib/api/client/interceptors.tsx +++ b/src/lib/api/client/interceptors.tsx @@ -1,20 +1,18 @@ import { AxiosRequestConfig } from 'axios' -// requestInterceptor.ts +import { useAuthStore } from '../../stores/useAuthStore' + export const requestInterceptor = ( config: AxiosRequestConfig ): AxiosRequestConfig => { - if (typeof window !== 'undefined') { - // 클라이언트 환경인지 확인 - const token: string | null = localStorage.getItem('token') + // Zustand에서 토큰 직접 가져오기 + const token = useAuthStore.getState().accessToken - const isAuthRequired = - config.url && - !(config.url.includes('/products') && !config.url.includes('/favorite')) + const isPublicEndpoint = + config.url?.startsWith('/products') && !config.url?.includes('/favorite') - if (token && isAuthRequired) { - config.headers = config.headers || {} - config.headers['Authorization'] = `Basic ${token}` - } + if (token && !isPublicEndpoint) { + config.headers = config.headers || {} + config.headers['Authorization'] = `Bearer ${token}` } return config diff --git a/src/lib/stores/useAuthStore.ts b/src/lib/stores/useAuthStore.ts new file mode 100644 index 00000000..4a7701f3 --- /dev/null +++ b/src/lib/stores/useAuthStore.ts @@ -0,0 +1,16 @@ +import { create } from 'zustand' + +interface AuthState { + accessToken: string | null + setAccessToken: (token: string) => void +} + +export const useAuthStore = create((set) => ({ + accessToken: null, + setAccessToken: (token) => { + set({ accessToken: token }) + if (typeof window !== 'undefined') { + localStorage.setItem('token', token) + } + }, +}))