diff --git a/package-lock.json b/package-lock.json index a1e590ee..5d1b4f78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,9 @@ "@testing-library/user-event": "^13.5.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.30.0", "react-scripts": "5.0.1", + "sass": "^1.89.0", "web-vitals": "^2.1.4" } }, @@ -3192,6 +3194,288 @@ "node": ">= 8" } }, + "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, + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.11", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.11.tgz", @@ -3241,6 +3525,14 @@ } } }, + "node_modules/@remix-run/router": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", + "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -6791,6 +7083,18 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "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==", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -9181,6 +9485,11 @@ "url": "https://opencollective.com/immer" } }, + "node_modules/immutable": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.2.tgz", + "integrity": "sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -12472,6 +12781,12 @@ "tslib": "^2.0.3" } }, + "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==", + "optional": true + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -14671,6 +14986,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.0.tgz", + "integrity": "sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==", + "dependencies": { + "@remix-run/router": "1.23.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.0.tgz", + "integrity": "sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==", + "dependencies": { + "@remix-run/router": "1.23.0", + "react-router": "6.30.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -15223,6 +15568,25 @@ "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" }, + "node_modules/sass": { + "version": "1.89.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.0.tgz", + "integrity": "sha512-ld+kQU8YTdGNjOLfRWBzewJpU5cwEv/h5yyqlSeJcj6Yh8U4TDA9UA5FPicqDz/xgRPWRSYIQNiFks21TbA9KQ==", + "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/sass-loader": { "version": "12.6.0", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", @@ -15260,6 +15624,32 @@ } } }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/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==", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", diff --git a/package.json b/package.json index 7ff0d6b5..5a00eb52 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "@testing-library/user-event": "^13.5.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.30.0", "react-scripts": "5.0.1", + "sass": "^1.89.0", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/public/assets/images/common/og_img.png b/public/assets/images/common/og_img.png new file mode 100644 index 00000000..093e85dc Binary files /dev/null and b/public/assets/images/common/og_img.png differ diff --git a/public/assets/images/main/bottom_banner_img.png b/public/assets/images/main/bottom_banner_img.png new file mode 100644 index 00000000..17b6df69 Binary files /dev/null and b/public/assets/images/main/bottom_banner_img.png differ diff --git a/public/assets/images/main/bottom_banner_img_mo.png b/public/assets/images/main/bottom_banner_img_mo.png new file mode 100644 index 00000000..1dcc0f82 Binary files /dev/null and b/public/assets/images/main/bottom_banner_img_mo.png differ diff --git a/public/assets/images/main/service_hot_img.png b/public/assets/images/main/service_hot_img.png new file mode 100644 index 00000000..2254efbe Binary files /dev/null and b/public/assets/images/main/service_hot_img.png differ diff --git a/public/assets/images/main/service_hot_img_mo.png b/public/assets/images/main/service_hot_img_mo.png new file mode 100644 index 00000000..129c373e Binary files /dev/null and b/public/assets/images/main/service_hot_img_mo.png differ diff --git a/public/assets/images/main/service_register_img.png b/public/assets/images/main/service_register_img.png new file mode 100644 index 00000000..dc949e56 Binary files /dev/null and b/public/assets/images/main/service_register_img.png differ diff --git a/public/assets/images/main/service_register_img_mo.png b/public/assets/images/main/service_register_img_mo.png new file mode 100644 index 00000000..bba8468c Binary files /dev/null and b/public/assets/images/main/service_register_img_mo.png differ diff --git a/public/assets/images/main/service_search_img.png b/public/assets/images/main/service_search_img.png new file mode 100644 index 00000000..a7f3a4f6 Binary files /dev/null and b/public/assets/images/main/service_search_img.png differ diff --git a/public/assets/images/main/service_search_img_mo.png b/public/assets/images/main/service_search_img_mo.png new file mode 100644 index 00000000..bbc1155c Binary files /dev/null and b/public/assets/images/main/service_search_img_mo.png differ diff --git a/public/assets/images/main/top_banner_img.png b/public/assets/images/main/top_banner_img.png new file mode 100644 index 00000000..07b45ec5 Binary files /dev/null and b/public/assets/images/main/top_banner_img.png differ diff --git a/public/assets/images/main/top_banner_img_mo.png b/public/assets/images/main/top_banner_img_mo.png new file mode 100644 index 00000000..86a64aff Binary files /dev/null and b/public/assets/images/main/top_banner_img_mo.png differ diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index a11777cc..00000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/public/index.html b/public/index.html index aa069f27..d81391ab 100644 --- a/public/index.html +++ b/public/index.html @@ -1,43 +1,34 @@ - + - - - - + + + + + + - - - - - React App + + + + + + + 판다마켓
- diff --git a/public/logo192.png b/public/logo192.png deleted file mode 100644 index fc44b0a3..00000000 Binary files a/public/logo192.png and /dev/null differ diff --git a/public/logo512.png b/public/logo512.png deleted file mode 100644 index a4e47a65..00000000 Binary files a/public/logo512.png and /dev/null differ diff --git a/public/manifest.json b/public/manifest.json deleted file mode 100644 index 080d6c77..00000000 --- a/public/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "short_name": "React App", - "name": "Create React App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 74b5e053..00000000 --- a/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/src/App.js b/src/App.js index 37845757..a3a5801a 100644 --- a/src/App.js +++ b/src/App.js @@ -1,24 +1,15 @@ -import logo from './logo.svg'; -import './App.css'; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import routes from "./routes"; +import "./styles/style.scss"; function App() { + const router = createBrowserRouter(routes, { + future: { + v7_relativeSplatPath: true, + }, + }); return ( -
-
- logo -

- Edit src/App.js and save to reload. -

- - Learn React - -
-
+ ); } diff --git a/src/App.test.js b/src/App.test.js deleted file mode 100644 index 1f03afee..00000000 --- a/src/App.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/src/assets/images/common/logo_lg.svg b/src/assets/images/common/logo_lg.svg new file mode 100644 index 00000000..a71db940 --- /dev/null +++ b/src/assets/images/common/logo_lg.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/assets/images/common/logo_md.svg b/src/assets/images/common/logo_md.svg new file mode 100644 index 00000000..9842d949 --- /dev/null +++ b/src/assets/images/common/logo_md.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/assets/images/common/logo_sm.svg b/src/assets/images/common/logo_sm.svg new file mode 100644 index 00000000..a0c381b1 --- /dev/null +++ b/src/assets/images/common/logo_sm.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/assets/images/common/logo_sx.svg b/src/assets/images/common/logo_sx.svg new file mode 100644 index 00000000..55a63efc --- /dev/null +++ b/src/assets/images/common/logo_sx.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icons/ic_facebook.svg b/src/assets/images/icons/ic_facebook.svg new file mode 100644 index 00000000..b9c9d493 --- /dev/null +++ b/src/assets/images/icons/ic_facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icons/ic_favorite.svg b/src/assets/images/icons/ic_favorite.svg new file mode 100644 index 00000000..cca65053 --- /dev/null +++ b/src/assets/images/icons/ic_favorite.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icons/ic_instagram.svg b/src/assets/images/icons/ic_instagram.svg new file mode 100644 index 00000000..0b9337b0 --- /dev/null +++ b/src/assets/images/icons/ic_instagram.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icons/ic_pagination_arrow.svg b/src/assets/images/icons/ic_pagination_arrow.svg new file mode 100644 index 00000000..378fecdb --- /dev/null +++ b/src/assets/images/icons/ic_pagination_arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icons/ic_pw_hide.svg b/src/assets/images/icons/ic_pw_hide.svg new file mode 100644 index 00000000..f039d5cc --- /dev/null +++ b/src/assets/images/icons/ic_pw_hide.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/icons/ic_pw_show.svg b/src/assets/images/icons/ic_pw_show.svg new file mode 100644 index 00000000..35a75305 --- /dev/null +++ b/src/assets/images/icons/ic_pw_show.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icons/ic_search.svg b/src/assets/images/icons/ic_search.svg new file mode 100644 index 00000000..52241e6d --- /dev/null +++ b/src/assets/images/icons/ic_search.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icons/ic_select_arrow.svg b/src/assets/images/icons/ic_select_arrow.svg new file mode 100644 index 00000000..8308690f --- /dev/null +++ b/src/assets/images/icons/ic_select_arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icons/ic_sns_google.svg b/src/assets/images/icons/ic_sns_google.svg new file mode 100644 index 00000000..5c5d1f2a --- /dev/null +++ b/src/assets/images/icons/ic_sns_google.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/icons/ic_sns_kakao.svg b/src/assets/images/icons/ic_sns_kakao.svg new file mode 100644 index 00000000..a9c381ff --- /dev/null +++ b/src/assets/images/icons/ic_sns_kakao.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/assets/images/icons/ic_sort.svg b/src/assets/images/icons/ic_sort.svg new file mode 100644 index 00000000..e4542d39 --- /dev/null +++ b/src/assets/images/icons/ic_sort.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/icons/ic_twitter.svg b/src/assets/images/icons/ic_twitter.svg new file mode 100644 index 00000000..14a6069a --- /dev/null +++ b/src/assets/images/icons/ic_twitter.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icons/ic_user_thumbnail.svg b/src/assets/images/icons/ic_user_thumbnail.svg new file mode 100644 index 00000000..94d40a49 --- /dev/null +++ b/src/assets/images/icons/ic_user_thumbnail.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/icons/ic_youtube.svg b/src/assets/images/icons/ic_youtube.svg new file mode 100644 index 00000000..c65eceb5 --- /dev/null +++ b/src/assets/images/icons/ic_youtube.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/components/AllProductArea/AllProductArea.js b/src/components/AllProductArea/AllProductArea.js new file mode 100644 index 00000000..d8c44bf1 --- /dev/null +++ b/src/components/AllProductArea/AllProductArea.js @@ -0,0 +1,135 @@ +import { useEffect, useState } from "react"; +import { Link } from "react-router-dom"; +import Pagination from "../Pagination/Pagination"; +import ProductList from "../ProductList/ProductList"; +import { getData } from "../../data/api"; +import styles from "./AllProductArea.module.scss"; +import { getItemCount } from "../../utils/getItemCount"; + +const SORT_TYPE = { + recent: "최신순", + favorite: "좋아요순", +}; +const SortDropdown = ({ orderBy, setOrderBy }) => { + const [sortOpen, setSortOpen] = useState(false); + const handleClickSort = () => setSortOpen(!sortOpen); + const handleSelectSort = (sort) => { + setOrderBy(sort); + setSortOpen(!sortOpen); + }; + + return ( + <> + + {sortOpen && ( +
+
    + {Object.keys(SORT_TYPE).map((sort) => ( +
  • + +
  • + ))} +
+
+ )} + + ); +}; + +const ITEM_COUNT = { + WEB: 10, + TABLET: 6, + MOBILE: 4, +}; + +const INIT_PAGE_SIZE = getItemCount(ITEM_COUNT); + +const AllProductArea = () => { + const [currentPage, setCurrentPage] = useState(1); + const [orderBy, setOrderBy] = useState("recent"); + const [pageSize, setPageSize] = useState(INIT_PAGE_SIZE); + const [productList, setProductList] = useState([]); + const [totalCount, setTotalCount] = useState(0); + + // 요구 정의서 + // 1. orderby="recent", 10가지 상품을 전체 상품 리스트에 렌더링 + // 2. 반응형에 따라 웹에선 10, 타블렛에선 6, 모바일에선 4 보여주기 (미디어 쿼리 사용하기) + // 3. 전체 상품에서 드롭다운으로 최신순/좋아요순 정렬 기능 추가 + // 4. [심화] 페이지네이션 기능 구현 + + const getProductList = async (options) => { + try { + const data = await getData(options); + if (!data) return; + setProductList(data.list); + setTotalCount(data.totalCount); + } catch (error) { + console.error(error); + } + }; + + const updatePageSize = () => { + const itemCount = getItemCount(ITEM_COUNT); + setPageSize(itemCount); + }; + + useEffect(() => { + updatePageSize(); + window.addEventListener("resize", updatePageSize); + + return () => { + window.removeEventListener("resize", updatePageSize); + }; + }, []); + + useEffect(() => { + getProductList({ + page: currentPage, + pageSize: pageSize, + orderBy: orderBy, + }); + }, [currentPage, pageSize, orderBy]); + + return ( + <> +
+

전체 상품

+
+
+ +
+
+ + 상품 등록하기 + +
+ +
+
+
+ + +
+ + ); +}; + +export default AllProductArea; diff --git a/src/components/AllProductArea/AllProductArea.module.scss b/src/components/AllProductArea/AllProductArea.module.scss new file mode 100644 index 00000000..a00dfaa9 --- /dev/null +++ b/src/components/AllProductArea/AllProductArea.module.scss @@ -0,0 +1,151 @@ +@use "../../styles/variables" as var; +@use "../../styles/mixin" as mixin; + +.allProductArea { + &__title { + font-size: 20px; + font-weight: 700; + color: var.$gray900; + } + + &__utils { + display: flex; + align-items: center; + gap: 12px; + + @include mixin.mobile { + flex-wrap: wrap; + gap: 8px 14px; + } + + .utils { + $height: 42px; + + &__searchBox { + width: 325px; + margin-left: auto; + + @include mixin.tablet { + width: 242px; + } + + @include mixin.mobile { + margin: 0; + width: calc(100% - 56px); + order: 3; + } + + input { + display: block; + width: 100%; + height: $height; + padding: 0 12px 0 44px; + background: var.$gray100 + url("../../assets/images/icons/ic_search.svg") 16px center no-repeat; + border-radius: 12px; + } + } + + &__productAdd { + display: block; + width: 132px; + line-height: $height; + text-align: center; + font-size: 16px; + font-weight: 600; + color: var.$gray100; + background: var.$primary-color; + border-radius: 8px; + + @include mixin.mobile { + margin-left: auto; + } + } + + &__sortSelectBox { + position: relative; + width: 130px; + + @include mixin.mobile { + width: auto; + order: 4; + } + + .sortSelectBox { + &__current { + display: block; + width: 100%; + padding: 0 42px 0 20px; + height: $height; + border: 1px solid var.$gray200; + font-size: 16px; + text-align: left; + color: var.$gray800; + background: url("../../assets/images/icons/ic_select_arrow.svg") + right 20px center no-repeat; + border-radius: 12px; + + @include mixin.mobile { + width: $height; + padding: 0; + background: url("../../assets/images/icons/ic_sort.svg") center + center no-repeat; + + span { + display: none; + } + } + } + + &__list { + position: absolute; + top: 100%; + right: 0; + width: 130px; + margin-top: 8px; + z-index: 1; + overflow: hidden; + + ul { + border: 1px solid var.$gray200; + background: var.$white; + border-radius: 12px; + transform: translateY(-100%); + animation: showSortList 0.3s ease forwards; + + @keyframes showSortList { + 100% { + transform: translateY(0); + } + } + + li { + border-bottom: 1px solid var.$gray200; + + &:last-of-type { + border-bottom: none; + } + + button { + display: block; + width: 100%; + height: $height; + font-size: 16px; + color: var.$gray800; + } + } + } + } + } + } + } + } + + &__content { + margin-top: 24px; + + @include mixin.mobile { + margin-top: 16px; + } + } +} diff --git a/src/components/AuthGuide/AuthGuide.js b/src/components/AuthGuide/AuthGuide.js new file mode 100644 index 00000000..f14ed6f3 --- /dev/null +++ b/src/components/AuthGuide/AuthGuide.js @@ -0,0 +1,18 @@ +import { Link } from "react-router-dom"; + +const AuthGuide = ({ guideTxt, linkTxt, linkUrl }) => { + return ( +
+

{guideTxt}

+ + {linkTxt} + +
+ ); +}; + +export default AuthGuide; diff --git a/src/components/AuthSns/AuthSns.js b/src/components/AuthSns/AuthSns.js new file mode 100644 index 00000000..983bbeb7 --- /dev/null +++ b/src/components/AuthSns/AuthSns.js @@ -0,0 +1,31 @@ +import { Link } from "react-router-dom"; +import snsGoogle from "../../assets/images/icons/ic_sns_google.svg"; +import snsKakao from "../../assets/images/icons/ic_sns_kakao.svg"; + +const AuthSns = () => { + return ( +
+

간편 로그인하기

+
    +
  • + + 구글 아이콘 + +
  • +
  • + + 카카오톡 아이콘 + +
  • +
+
+ ); +}; + +export default AuthSns; diff --git a/src/components/BestProductArea/BestProductArea.js b/src/components/BestProductArea/BestProductArea.js new file mode 100644 index 00000000..6759a4ba --- /dev/null +++ b/src/components/BestProductArea/BestProductArea.js @@ -0,0 +1,58 @@ +import { useEffect, useState } from "react"; +import ProductList from "../ProductList/ProductList"; +import { getData } from "../../data/api"; +import styles from "./BestProductArea.module.scss"; +import { getItemCount } from "../../utils/getItemCount"; + +const INIT_PAGE_SIZE = 4; +const ITEM_COUNT = { + WEB: 4, + TABLET: 2, + MOBILE: 1, +}; + +const BestProductArea = () => { + const [bestList, setBestList] = useState([]); + const [pageSize, setPageSize] = useState(INIT_PAGE_SIZE); + + // 요구 정의서 + // 1. orderby="favorite", 4가지 상품을 베스트 상품 리스트에 렌더링 + // 2. 반응형에 따라 웹에선 4, 타블렛에선 2, 모바일에선 1 보여주기 (미디어 쿼리 사용하기) + + const getProductList = async (options) => { + try { + const data = await getData(options); + if (!data) return; + setBestList(data.list); + } catch (error) { + console.error(error); + } + }; + + const updatePageSize = () => { + const itemCount = getItemCount(ITEM_COUNT); + setPageSize(itemCount); + }; + + useEffect(() => { + getProductList({ orderBy: "favorite", pageSize: INIT_PAGE_SIZE, page: 1 }); + + updatePageSize(); + window.addEventListener("resize", updatePageSize); + + return () => { + window.removeEventListener("resize", updatePageSize); + }; + }, []); + + return ( + <> +

베스트 상품

+
+ +
+ + ); +}; + +export default BestProductArea; diff --git a/src/components/BestProductArea/BestProductArea.module.scss b/src/components/BestProductArea/BestProductArea.module.scss new file mode 100644 index 00000000..394a2702 --- /dev/null +++ b/src/components/BestProductArea/BestProductArea.module.scss @@ -0,0 +1,13 @@ +@use "../../styles/variables" as var; + +.bestProductArea { + &__title { + font-size: 20px; + font-weight: 700; + color: var.$gray900; + } + + &__content { + margin-top: 16px; + } +} diff --git a/src/components/Pagination/Pagination.js b/src/components/Pagination/Pagination.js new file mode 100644 index 00000000..f93c87ba --- /dev/null +++ b/src/components/Pagination/Pagination.js @@ -0,0 +1,89 @@ +import { useCallback, useMemo } from "react"; +import arrow from "../../assets/images/icons/ic_pagination_arrow.svg"; +import styles from "./Pagination.module.scss"; + +const LIMIT = 5; +const Pagination = ({ pageSize, totalCount, currentPage, setCurrentPage }) => { + // 페이지네이션 5개(LIMIT)로 끊어서 2차원 배열로 생성 + const calcPager = useMemo(() => { + if (!totalCount) return; + + const totalPager = Math.ceil(totalCount / pageSize); + + const pagerArr = []; + let pagerCounter = 0; + for (let i = 0; i < totalPager; i++) { + if (!pagerArr[pagerCounter]) pagerArr[pagerCounter] = []; + pagerArr[pagerCounter].push(i + 1); + if (pagerArr[pagerCounter].length === LIMIT) pagerCounter++; + } + + return { pagerArr, totalPager }; + }, [pageSize, totalCount]); + + // currentPage 값이 있는 배열 반환 + const getCurrentGroup = useCallback(() => { + if (!calcPager) return; + + return calcPager.pagerArr.filter((item) => item.includes(currentPage))[0]; + }, [calcPager, currentPage]); + + const currentGroup = getCurrentGroup(); + + // prev 버튼 클릭 + const handleClickPrev = () => { + const changeCurrentPage = currentPage - 1 <= 0 ? 1 : currentPage - 1; + setCurrentPage(changeCurrentPage); + }; + + // next 버튼 클릭 + const handleClickNext = () => { + const totalPageNum = calcPager.totalPager; + const changeCurrentPage = + currentPage + 1 >= totalPageNum ? totalPageNum : currentPage + 1; + setCurrentPage(changeCurrentPage); + }; + + return ( +
+ {currentGroup && ( + <> + +
    + {currentGroup.map((pager) => ( +
  • + +
  • + ))} +
+ + + )} +
+ ); +}; + +export default Pagination; diff --git a/src/components/Pagination/Pagination.module.scss b/src/components/Pagination/Pagination.module.scss new file mode 100644 index 00000000..75ca0da8 --- /dev/null +++ b/src/components/Pagination/Pagination.module.scss @@ -0,0 +1,47 @@ +@use "../../styles/variables" as var; + +$size: 40px; + +.pagination { + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + margin-top: 40px; + + &__list { + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + } + + &__button { + display: flex; + align-items: center; + justify-content: center; + width: $size; + height: $size; + border: 1px solid var.$gray200; + font-size: 16px; + font-weight: 600; + color: var.$gray500; + border-radius: 50%; + + &:disabled { + opacity: 0.5; + } + + &-current { + border-color: #2f80ed; + color: #fff; + background: #2f80ed; + } + + &-next { + img { + transform: rotate(180deg); + } + } + } +} diff --git a/src/components/ProductItem/ProductItem.js b/src/components/ProductItem/ProductItem.js new file mode 100644 index 00000000..b3808a2c --- /dev/null +++ b/src/components/ProductItem/ProductItem.js @@ -0,0 +1,23 @@ +import { Link } from "react-router-dom"; +import style from "./ProductItem.module.scss"; + +const ProductItem = ({ product }) => { + const { id, images, name, price, favoriteCount } = product; + + return ( + +
+ {name} +
+
+

{name}

+ + {price.toLocaleString("ko-KR")}원 + + {favoriteCount} +
+ + ); +}; + +export default ProductItem; diff --git a/src/components/ProductItem/ProductItem.module.scss b/src/components/ProductItem/ProductItem.module.scss new file mode 100644 index 00000000..69d45db0 --- /dev/null +++ b/src/components/ProductItem/ProductItem.module.scss @@ -0,0 +1,52 @@ +@use "../../styles/variables" as var; +@use "../../styles/mixin" as mixin; + +.productItem { + display: block; + + &__image { + position: relative; + padding-top: 100%; + overflow: hidden; + border-radius: 16px; + + img { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + object-fit: cover; + } + } + + &__content { + margin-top: 16px; + color: var.$gray800; + } + + &__subject { + font-size: 14px; + font-weight: 500; + @include mixin.ellipsis(); + } + + &__price { + display: block; + margin-top: 6px; + font-size: 16px; + font-weight: 700; + } + + &__favorite { + display: block; + margin-top: 6px; + padding-left: 20px; + font-size: 12px; + font-weight: 500; + color: var.$gray600; + line-height: 1.5; + background: url("../../assets/images/icons/ic_favorite.svg") left center + no-repeat; + } +} diff --git a/src/components/ProductList/ProductList.js b/src/components/ProductList/ProductList.js new file mode 100644 index 00000000..65f1ce12 --- /dev/null +++ b/src/components/ProductList/ProductList.js @@ -0,0 +1,18 @@ +import ProductItem from "../ProductItem/ProductItem"; +import styles from "./ProductList.module.scss"; + +const ProductList = ({ list, type = "all" }) => { + return ( + + ); +}; + +export default ProductList; diff --git a/src/components/ProductList/ProductList.module.scss b/src/components/ProductList/ProductList.module.scss new file mode 100644 index 00000000..46040f20 --- /dev/null +++ b/src/components/ProductList/ProductList.module.scss @@ -0,0 +1,32 @@ +@use "../../styles/mixin" as mixin; + +.productList { + display: grid; + gap: 40px 24px; + + &__best { + grid-template-columns: repeat(4, 1fr); + + @include mixin.tablet { + grid-template-columns: repeat(2, 1fr); + gap: 10px; + } + @include mixin.mobile { + grid-template-columns: repeat(1, 1fr); + } + } + + &__all { + grid-template-columns: repeat(5, 1fr); + + @include mixin.tablet { + gap: 16px; + grid-template-columns: repeat(3, 1fr); + } + + @include mixin.mobile { + gap: 8px; + grid-template-columns: repeat(2, 1fr); + } + } +} diff --git a/src/data/api.js b/src/data/api.js new file mode 100644 index 00000000..b0c9d7a1 --- /dev/null +++ b/src/data/api.js @@ -0,0 +1,17 @@ +const BASE_URL = "https://panda-market-api.vercel.app"; + +export const getData = async ({ + page = 1, + pageSize = 10, + orderBy = "recent", +}) => { + const query = `page=${page}&pageSize=${pageSize}&orderBy=${orderBy}`; + const res = await fetch(`${BASE_URL}/products?${query}`); + + if (!res.ok) { + throw new Error("상품 리스트를 불러오는데 실패했습니다."); + } + + const data = await res.json(); + return data; +}; diff --git a/src/hooks/useAllValid.js b/src/hooks/useAllValid.js new file mode 100644 index 00000000..59e6408d --- /dev/null +++ b/src/hooks/useAllValid.js @@ -0,0 +1,17 @@ +import { useEffect, useState } from "react"; + +const useAllValid = (valueValids) => { + const [isAllValid, setIsAllValid] = useState(false); + + useEffect(() => { + // auth 관련 페이지에서만 검증 요소가 전부 true인지 확인 + const allValid = Object.keys(valueValids).every((valid) => { + return valueValids[valid].isValid; + }); + setIsAllValid(allValid); + }, [valueValids]); + + return isAllValid; +}; + +export default useAllValid; diff --git a/src/hooks/usePasswordToggle.js b/src/hooks/usePasswordToggle.js new file mode 100644 index 00000000..106e63d8 --- /dev/null +++ b/src/hooks/usePasswordToggle.js @@ -0,0 +1,21 @@ +import { useState } from "react"; +import pwShow from "../assets/images/icons/ic_pw_show.svg"; +import pwHide from "../assets/images/icons/ic_pw_hide.svg"; + +const usePasswordToggle = () => { + const [toggle, setToggle] = useState(false); + + const handleClickToggle = () => { + setToggle(() => !toggle); + }; + + const toggleImg = toggle ? pwShow : pwHide; + + return { + toggle, + handleClickToggle, + toggleImg, + }; +}; + +export default usePasswordToggle; diff --git a/src/index.css b/src/index.css deleted file mode 100644 index ec2585e8..00000000 --- a/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/src/index.js b/src/index.js index d563c0fb..8db5acb8 100644 --- a/src/index.js +++ b/src/index.js @@ -1,17 +1,10 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; -const root = ReactDOM.createRoot(document.getElementById('root')); +const root = ReactDOM.createRoot(document.getElementById("root")); root.render( ); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/src/layouts/Footer.js b/src/layouts/Footer.js new file mode 100644 index 00000000..b56d11ef --- /dev/null +++ b/src/layouts/Footer.js @@ -0,0 +1,70 @@ +import { Link } from "react-router-dom"; +import icFacebook from "../assets/images/icons/ic_facebook.svg"; +import icTwitter from "../assets/images/icons/ic_twitter.svg"; +import icYoutube from "../assets/images/icons/ic_youtube.svg"; +import icInstagram from "../assets/images/icons/ic_instagram.svg"; + +const snsImgList = { + facebook: icFacebook, + twitter: icTwitter, + youtube: icYoutube, + instagram: icInstagram, +}; + +const Footer = () => { + return ( +
+
+

©codeit - 2024

+
    +
  • + Privacy Policy +
  • +
  • + FAQ +
  • +
+
    +
  • + + + +
  • +
  • + + + +
  • +
  • + + + +
  • +
  • + + + +
  • +
+
+
+ ); +}; + +export default Footer; diff --git a/src/layouts/Header.js b/src/layouts/Header.js new file mode 100644 index 00000000..03191757 --- /dev/null +++ b/src/layouts/Header.js @@ -0,0 +1,64 @@ +import { Link, useLocation } from "react-router-dom"; +import getLogo from "../utils/getLogo"; +import userThumbnail from "../assets/images/icons/ic_user_thumbnail.svg"; +import { useState } from "react"; + +const GNB_MENU = [ + { path: "/free", title: "자유게시판" }, + { path: "/items", title: "중고마켓" }, +]; + +const Header = () => { + const location = useLocation(); + const [isLogin, setIsLogin] = useState(true); + + return ( + + ); +}; + +export default Header; diff --git a/src/layouts/Layout.js b/src/layouts/Layout.js new file mode 100644 index 00000000..ae93189d --- /dev/null +++ b/src/layouts/Layout.js @@ -0,0 +1,13 @@ +import { Outlet } from "react-router"; +import Header from "./Header"; + +const Layout = () => { + return ( +
+
+ +
+ ); +}; + +export default Layout; diff --git a/src/logo.svg b/src/logo.svg deleted file mode 100644 index 9dfc1c05..00000000 --- a/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/pages/AddItemPage/AddItemPage.js b/src/pages/AddItemPage/AddItemPage.js new file mode 100644 index 00000000..77d2c394 --- /dev/null +++ b/src/pages/AddItemPage/AddItemPage.js @@ -0,0 +1,5 @@ +const AddItemPage = () => { + return
AddItemPage
; +}; + +export default AddItemPage; diff --git a/src/pages/FaqPage/FaqPage.js b/src/pages/FaqPage/FaqPage.js new file mode 100644 index 00000000..58da441a --- /dev/null +++ b/src/pages/FaqPage/FaqPage.js @@ -0,0 +1,5 @@ +const FaqPage = () => { + return
faq 페이지
; +}; + +export default FaqPage; diff --git a/src/pages/ItemsPage/ItemsPage.js b/src/pages/ItemsPage/ItemsPage.js new file mode 100644 index 00000000..a03dac54 --- /dev/null +++ b/src/pages/ItemsPage/ItemsPage.js @@ -0,0 +1,20 @@ +import styles from "./ItemsPage.module.scss"; +import AllProductArea from "../../components/AllProductArea/AllProductArea"; +import BestProductArea from "../../components/BestProductArea/BestProductArea"; + +const ItemsPage = () => { + return ( +
+
+
+ +
+
+ +
+
+
+ ); +}; + +export default ItemsPage; diff --git a/src/pages/ItemsPage/ItemsPage.module.scss b/src/pages/ItemsPage/ItemsPage.module.scss new file mode 100644 index 00000000..0f4c86fd --- /dev/null +++ b/src/pages/ItemsPage/ItemsPage.module.scss @@ -0,0 +1,27 @@ +@use "../../styles/variables" as var; +@use "../../styles/mixin" as mixin; + +.itemsPage { + &__inner { + width: calc(100% - 48px); + max-width: 1200px; + margin: 24px auto; + + @include mixin.mobile { + width: calc(100% - 32px); + margin-top: 17px; + } + } + + &__section { + margin-bottom: 40px; + + &:last-of-type { + margin-bottom: 0; + } + + @include mixin.mobile { + margin-bottom: 24px; + } + } +} diff --git a/src/pages/LoginPage/LoginPage.js b/src/pages/LoginPage/LoginPage.js new file mode 100644 index 00000000..0fd537c9 --- /dev/null +++ b/src/pages/LoginPage/LoginPage.js @@ -0,0 +1,149 @@ +import { useState } from "react"; +import { Link, useNavigate } from "react-router-dom"; +import useAllValid from "../../hooks/useAllValid"; +import usePasswordToggle from "../../hooks/usePasswordToggle"; +import { + checkValidEmail, + checkValidPassword, + getAuthValidClassName, +} from "../../utils/authUtils"; +import getLogo from "../../utils/getLogo"; +import AuthSns from "../../components/AuthSns/AuthSns"; +import AuthGuide from "../../components/AuthGuide/AuthGuide"; +import "../../styles/auth.scss"; +import styles from "./LoginPage.module.scss"; + +const INIT_VALUE = { email: "", password: "" }; +const INIT_VALID = { + email: { + isValid: null, + msg: "", + }, + password: { + isValid: null, + msg: "", + }, +}; + +const VALIDATOR = { + email: checkValidEmail, + password: checkValidPassword, +}; + +const LoginPage = () => { + const nav = useNavigate(); + const [userValues, setUserValues] = useState(INIT_VALUE); + const [valueValids, setValueValids] = useState(INIT_VALID); + const pwToggle = usePasswordToggle(); + const isAllValid = useAllValid(valueValids); + + const handleChangeUserValues = (e) => { + setUserValues({ ...userValues, [e.target.name]: e.target.value }); + }; + + const handleFocusOut = (e) => { + const { name, value } = e.target; + + // 검증할 요소인지 확인 + if (!VALIDATOR[name]) return; + + setValueValids(() => ({ + ...valueValids, + [name]: VALIDATOR[name](value), + })); + }; + + const handleClickSubmit = (e) => { + e.preventDefault(); + nav("/"); + }; + + return ( +
+
+

+ + 판다마켓 로고 이미지 + +

+
+ {/* 이메일 */} +
+ +
+ +
+ {!valueValids.email.isValid && ( +

{valueValids.email.msg}

+ )} +
+ {/* 비밀번호 */} +
+ +
+ + +
+ {!valueValids.password.isValid && ( +

{valueValids.password.msg}

+ )} +
+ +
+ + +
+
+ ); +}; + +export default LoginPage; diff --git a/src/pages/LoginPage/LoginPage.module.scss b/src/pages/LoginPage/LoginPage.module.scss new file mode 100644 index 00000000..62fdafba --- /dev/null +++ b/src/pages/LoginPage/LoginPage.module.scss @@ -0,0 +1,13 @@ +@use "../../styles/mixin" as mixin; + +.loginPage { + padding: 231px 0; + + @include mixin.tablet { + padding: 190px 0; + } + + @include mixin.mobile { + padding: 80px 0; + } +} diff --git a/src/pages/MainPage/MainPage.js b/src/pages/MainPage/MainPage.js new file mode 100644 index 00000000..62fb633e --- /dev/null +++ b/src/pages/MainPage/MainPage.js @@ -0,0 +1,163 @@ +import { Link } from "react-router-dom"; +import styles from "./MainPage.module.scss"; +import Footer from "../../layouts/Footer"; + +const MainPage = () => { + return ( + <> +
+ {/* sec__top-banner */} +
+
+
+

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

+ + 구경하러 가기 + +
+
+ 판다가 손을 들고 있는 이미지 +
+
+
+ {/* sec__service */} +
+
+ {/* service__item-hot */} +
+
+
+ Hot item 서비스 이미지 +
+
+ Hot item +

+ 인기 상품을 +
확인해 보세요 +

+

+ 가장 HOT한 중고거래 물품을 판다 마켓에서 확인해 보세요 +

+
+
+
+ {/* service__item-search */} +
+
+
+ Search 서비스 이미지 +
+
+ Search +

+ 구매를 원하는
+ 상품을 검색하세요 +

+

+ 구매하고 싶은 물품은 검색해서 쉽게 찾아보세요 +

+
+
+
+ {/* service__item-register */} +
+
+
+ Register 서비스 이미지 +
+
+ Register +

+ 판매를 원하는
+ 상품을 등록하세요 +

+

+ 어떤 물건이든 판매하고 싶은 상품을 쉽게 등록하세요 +

+
+
+
+
+
+ {/* sec__bottom-banner */} +
+
+
+

+ 믿을 수 있는
+ 판다마켓 중고 거래 +

+
+
+ 배너 영역의 판다 캐릭터끼리 대화 하는 모습이 담긴 이미지 +
+
+
+
+