diff --git a/package-lock.json b/package-lock.json index 16d337ab5..9aaf27d11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,27 @@ { - "name": "sprint5", + "name": "pandamarket", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "sprint5", + "name": "pandamarket", "version": "0.1.0", "dependencies": { "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.7.7", + "format": "^0.2.2", "react": "^18.3.1", "react-dom": "^18.3.1", "react-js-pagination": "^3.0.3", + "react-number": "^0.0.1-alpha4", + "react-number-format": "^5.4.2", "react-responsive": "^10.0.0", "react-router-dom": "^6.28.0", "react-scripts": "5.0.1", + "styled-components": "^6.1.13", "web-vitals": "^2.1.4" } }, @@ -2385,6 +2389,27 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "license": "MIT" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", @@ -4189,6 +4214,12 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "license": "MIT" }, + "node_modules/@types/stylis": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==", + "license": "MIT" + }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.9", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", @@ -5743,6 +5774,15 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -6292,6 +6332,15 @@ "postcss": "^8.4" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", @@ -6447,6 +6496,17 @@ "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", "license": "MIT" }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "license": "MIT", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -8674,6 +8734,14 @@ "node": ">= 6" } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -14270,6 +14338,22 @@ "node": ">=0.10.0" } }, + "node_modules/react-number": { + "version": "0.0.1-alpha4", + "resolved": "https://registry.npmjs.org/react-number/-/react-number-0.0.1-alpha4.tgz", + "integrity": "sha512-GEsyMEfen+gBAo/gC/PPRctOZnHLuqE/Ccr5yqx4hgagMRzF+nirLna+ZKrY17z1BNjMqeKooLXKtKPYZm8W8Q==", + "license": "ISC" + }, + "node_modules/react-number-format": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.4.2.tgz", + "integrity": "sha512-cg//jVdS49PYDgmcYoBnMMHl4XNTMuV723ZnHD2aXYtWWWqbVF3hjQ8iB+UZEuXapLbeA8P8H+1o6ZB1lcw3vg==", + "license": "MIT", + "peerDependencies": { + "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -15256,6 +15340,12 @@ "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==", "license": "MIT" }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -15872,6 +15962,68 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.13.tgz", + "integrity": "sha512-M0+N2xSnAtwcVAQeFEsGWFFxXDftHUD7XrKla06QbpUMmbmtFBMMTcKWvFXtWxuD5qQkB8iU5gk6QASlx2ZRMw==", + "license": "MIT", + "dependencies": { + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", + "css-to-react-native": "3.2.0", + "csstype": "3.1.3", + "postcss": "8.4.38", + "shallowequal": "1.1.0", + "stylis": "4.3.2", + "tslib": "2.6.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/styled-components/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "license": "0BSD" + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -15888,6 +16040,12 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", + "license": "MIT" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", diff --git a/package.json b/package.json index 845a82052..2f15f28da 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "sprint5", + "name": "pandamarket", "version": "0.1.0", "private": true, "dependencies": { @@ -7,12 +7,16 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.7.7", + "format": "^0.2.2", "react": "^18.3.1", "react-dom": "^18.3.1", "react-js-pagination": "^3.0.3", + "react-number": "^0.0.1-alpha4", + "react-number-format": "^5.4.2", "react-responsive": "^10.0.0", "react-router-dom": "^6.28.0", "react-scripts": "5.0.1", + "styled-components": "^6.1.13", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/src/api.js b/src/Api/api.js similarity index 77% rename from src/api.js rename to src/Api/api.js index dd9b6f565..5c4ebcd7b 100644 --- a/src/api.js +++ b/src/Api/api.js @@ -1,4 +1,4 @@ -async function productData({ orderBy, pageSize, search, page }) { +async function getProductData({ orderBy, pageSize, search, page }) { const query = `page=${page}&orderBy=${orderBy}&pageSize=${pageSize}&keyword=${search}`; const response = await fetch( `https://panda-market-api.vercel.app/products?${query}` @@ -11,4 +11,4 @@ async function productData({ orderBy, pageSize, search, page }) { return body; } -export default productData; +export default getProductData; diff --git a/src/App.js b/src/App.js deleted file mode 100644 index edf7f92b0..000000000 --- a/src/App.js +++ /dev/null @@ -1,26 +0,0 @@ -import "./Styles/App/Reset.css"; -import { BrowserRouter, Routes, Route } from "react-router-dom"; -import Home from "./Components/Main/Home"; -import ItemsListPage from "./Components/ProductList/ItemsList"; -import ScrollToTop from "./Components/App/ScrollToTop"; - -function App() { - return ( - <> - - - - } /> - } /> - - - - ); -} - -export default App; diff --git a/src/Assets/images/common/no_img.jpg b/src/Assets/images/app/common/no_img.jpg similarity index 100% rename from src/Assets/images/common/no_img.jpg rename to src/Assets/images/app/common/no_img.jpg diff --git a/src/Assets/images/home/Img_home_01.png b/src/Assets/images/app/home/Img_home_01.png similarity index 100% rename from src/Assets/images/home/Img_home_01.png rename to src/Assets/images/app/home/Img_home_01.png diff --git a/src/Assets/images/home/Img_home_02.png b/src/Assets/images/app/home/Img_home_02.png similarity index 100% rename from src/Assets/images/home/Img_home_02.png rename to src/Assets/images/app/home/Img_home_02.png diff --git a/src/Assets/images/home/Img_home_03.png b/src/Assets/images/app/home/Img_home_03.png similarity index 100% rename from src/Assets/images/home/Img_home_03.png rename to src/Assets/images/app/home/Img_home_03.png diff --git a/src/Assets/images/home/Img_home_bottom.png b/src/Assets/images/app/home/Img_home_bottom.png similarity index 100% rename from src/Assets/images/home/Img_home_bottom.png rename to src/Assets/images/app/home/Img_home_bottom.png diff --git a/src/Assets/images/home/Img_home_top.png b/src/Assets/images/app/home/Img_home_top.png similarity index 100% rename from src/Assets/images/home/Img_home_top.png rename to src/Assets/images/app/home/Img_home_top.png diff --git a/src/Assets/images/home/ic_facebook.svg b/src/Assets/images/app/home/ic_facebook.svg similarity index 100% rename from src/Assets/images/home/ic_facebook.svg rename to src/Assets/images/app/home/ic_facebook.svg diff --git a/src/Assets/images/home/ic_instagram.svg b/src/Assets/images/app/home/ic_instagram.svg similarity index 100% rename from src/Assets/images/home/ic_instagram.svg rename to src/Assets/images/app/home/ic_instagram.svg diff --git a/src/Assets/images/home/ic_twitter.svg b/src/Assets/images/app/home/ic_twitter.svg similarity index 100% rename from src/Assets/images/home/ic_twitter.svg rename to src/Assets/images/app/home/ic_twitter.svg diff --git a/src/Assets/images/home/ic_youtube.svg b/src/Assets/images/app/home/ic_youtube.svg similarity index 100% rename from src/Assets/images/home/ic_youtube.svg rename to src/Assets/images/app/home/ic_youtube.svg diff --git a/src/Assets/images/navi/logo.svg b/src/Assets/images/app/navi/logo.svg similarity index 100% rename from src/Assets/images/navi/logo.svg rename to src/Assets/images/app/navi/logo.svg diff --git a/src/Assets/images/navi/profile_default.png b/src/Assets/images/app/navi/profile_default.png similarity index 100% rename from src/Assets/images/navi/profile_default.png rename to src/Assets/images/app/navi/profile_default.png diff --git a/src/Assets/images/pagination/arrow_left.svg b/src/Assets/images/app/pagination/arrow_left.svg similarity index 100% rename from src/Assets/images/pagination/arrow_left.svg rename to src/Assets/images/app/pagination/arrow_left.svg diff --git a/src/Assets/images/pagination/arrow_right.svg b/src/Assets/images/app/pagination/arrow_right.svg similarity index 100% rename from src/Assets/images/pagination/arrow_right.svg rename to src/Assets/images/app/pagination/arrow_right.svg diff --git a/src/Assets/images/loginRegistration/btn_invisible.svg b/src/Assets/images/loginRegistration/btn_invisible.svg new file mode 100644 index 000000000..332dde2b4 --- /dev/null +++ b/src/Assets/images/loginRegistration/btn_invisible.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/Assets/images/loginRegistration/btn_visible.svg b/src/Assets/images/loginRegistration/btn_visible.svg new file mode 100644 index 000000000..35a75305e --- /dev/null +++ b/src/Assets/images/loginRegistration/btn_visible.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/Assets/images/loginRegistration/google.svg b/src/Assets/images/loginRegistration/google.svg new file mode 100644 index 000000000..4c021e65d --- /dev/null +++ b/src/Assets/images/loginRegistration/google.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/Assets/images/loginRegistration/kakao.svg b/src/Assets/images/loginRegistration/kakao.svg new file mode 100644 index 000000000..a1ed4c35c --- /dev/null +++ b/src/Assets/images/loginRegistration/kakao.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/Assets/images/loginRegistration/login_logo.png b/src/Assets/images/loginRegistration/login_logo.png new file mode 100644 index 000000000..1c5cd7cc1 Binary files /dev/null and b/src/Assets/images/loginRegistration/login_logo.png differ diff --git a/src/Assets/images/productList/not_found.png b/src/Assets/images/productList/not_found.png new file mode 100644 index 000000000..3daf6827e Binary files /dev/null and b/src/Assets/images/productList/not_found.png differ diff --git a/src/Assets/images/productRgs/cancel.svg b/src/Assets/images/productRgs/cancel.svg new file mode 100644 index 000000000..8ff53504e --- /dev/null +++ b/src/Assets/images/productRgs/cancel.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/Assets/images/productRgs/upload.svg b/src/Assets/images/productRgs/upload.svg new file mode 100644 index 000000000..5bb9abf55 --- /dev/null +++ b/src/Assets/images/productRgs/upload.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/Components/App/App.js b/src/Components/App/App.js new file mode 100644 index 000000000..2ac941788 --- /dev/null +++ b/src/Components/App/App.js @@ -0,0 +1,7 @@ +import { Outlet } from "react-router-dom"; + +function App() { + return ; +} + +export default App; diff --git a/src/Components/App/EmailValidation.js b/src/Components/App/EmailValidation.js new file mode 100644 index 000000000..1c0048485 --- /dev/null +++ b/src/Components/App/EmailValidation.js @@ -0,0 +1,8 @@ +// 이메일 유효성 체크 +function emailCheck(email) { + let regex = + /([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/; + return email !== "" && email !== "undefined" && regex.test(email); +} + +export default emailCheck; diff --git a/src/Components/App/Footer.js b/src/Components/App/Footer.js new file mode 100644 index 000000000..c023fae2b --- /dev/null +++ b/src/Components/App/Footer.js @@ -0,0 +1,36 @@ +import styles from "../../Styles/App/Footer.module.css"; +import { Link } from "react-router-dom"; +import iconInsta from "../../Assets/images/app/home/ic_instagram.svg"; +import iconFacebook from "../../Assets/images/app/home/ic_facebook.svg"; +import iconYoutube from "../../Assets/images/app/home/ic_youtube.svg"; +import iconTwitter from "../../Assets/images/app/home/ic_twitter.svg"; + +function Footer() { + return ( + + ); +} + +export default Footer; diff --git a/src/Components/App/ItemListNav.js b/src/Components/App/ItemListNav.js new file mode 100644 index 000000000..5e616f787 --- /dev/null +++ b/src/Components/App/ItemListNav.js @@ -0,0 +1,52 @@ +import styles from "../../Styles/App/Navi.module.css"; +import navLogo from "../../Assets/images/app/navi/logo.svg"; +import profileDefaultImg from "../../Assets/images/app/navi/profile_default.png"; +import { Link, NavLink } from "react-router-dom"; + +function getLinkStyle({ isActive }) { + return { + color: isActive ? "#3692ff" : undefined, + }; +} + +// 중고마켓 페이지 네비게이션 - /items +function ItemListNav() { + return ( +
+ +
+ ); +} + +export default ItemListNav; diff --git a/src/Components/App/Loading.js b/src/Components/App/Loading.js new file mode 100644 index 000000000..ca6af766d --- /dev/null +++ b/src/Components/App/Loading.js @@ -0,0 +1,12 @@ +import styles from "../../Styles/App/Loading.module.css"; +import React from "react"; + +export const Loading = () => { + return ( +
+ +
+ ); +}; + +export default Loading; diff --git a/src/Components/App/Navi.js b/src/Components/App/Navi.js deleted file mode 100644 index ea1efb32c..000000000 --- a/src/Components/App/Navi.js +++ /dev/null @@ -1,35 +0,0 @@ -import styles from "../../Styles/App/Navi.module.css"; -import navLogo from "../../Assets/images/navi/logo.svg"; -import profileDefaultImg from "../../Assets/images/navi/profile_default.png"; -import { Link } from "react-router-dom"; - -// 중고마켓 페이지 네비게이션 - /items -function ItemListNav() { - return ( -
- -
- ); -} - -export default ItemListNav; diff --git a/src/Components/App/Pagination.js b/src/Components/App/Pagination.js index b150e7d4a..a430c1d15 100644 --- a/src/Components/App/Pagination.js +++ b/src/Components/App/Pagination.js @@ -1,21 +1,48 @@ -import styles from "../../Styles/App/Pagination.css"; -import Pagination from "react-js-pagination"; +import styles from "../../Styles/App/Pagination.module.css"; +import arrowPrevImg from "../../Assets/images/app/pagination/arrow_left.svg"; +import arrowNextImg from "../../Assets/images/app/pagination/arrow_right.svg"; -function HandlePagiNation({ page, setPage, pageCount }) { - const handlePageChange = (page) => { - setPage(page); - }; +function PaginationContainer({ page, setPage, pageCount, isDataCount }) { + const itemCountPerPage = Math.ceil(pageCount / isDataCount); // 페이지 당 보여줄 데이터 개수 + const btnPage = 5; // 한 페이지당 pagination 5개 출력 + const currentSet = Math.ceil(page / btnPage); + const totalPages = Math.ceil(pageCount / isDataCount); + const noPrev = page === 1; + const noNext = page + itemCountPerPage - 1 >= totalPages; + const startPage = (currentSet - 1) * btnPage + 1; + const endPage = Math.min(startPage + btnPage - 1, totalPages); return ( - + ); } -export default HandlePagiNation; +export default PaginationContainer; diff --git a/src/Components/Login/LoginForm.js b/src/Components/Login/LoginForm.js new file mode 100644 index 000000000..fa6fe7a31 --- /dev/null +++ b/src/Components/Login/LoginForm.js @@ -0,0 +1,185 @@ +import { Link } from "react-router-dom"; +import { useEffect, useState } from "react"; +import EmailValidation from "../App/EmailValidation"; +import styles from "../../Styles/loginRegistration/loginRegistration.module.css"; +import LogoImg from "../../Assets/images/loginRegistration/login_logo.png"; +import GoogleImg from "../../Assets/images/loginRegistration/google.svg"; +import KaKaoImg from "../../Assets/images/loginRegistration/kakao.svg"; +import InVisibleImg from "../../Assets/images/loginRegistration/btn_invisible.svg"; +import VisibleImg from "../../Assets/images/loginRegistration/btn_visible.svg"; + +function LoginForm() { + const [getId, setGetId] = useState(""); + const [isEmail, setIsEmail] = useState(""); + const [isPassword, setIsPassword] = useState(""); + const [emailWarnMsg, setEmailWarnMsg] = useState(""); + const [passwordWarnMsg, setPasswordWarnMsg] = useState(""); + const [emailRequiredChk, setEmailRequiredChk] = useState(true); + const [passwordRequiredChk, setPasswordRequiredChk] = useState(true); + const [emailErrorChk, setEmailErrorChk] = useState(false); + const [passwordErrorChk, setPasswordErrorChk] = useState(false); + const [passwordVisible, setPasswordVisible] = useState(false); + const [isSubmit, setIsSubmit] = useState(""); + + const getEmailInfo = (e) => { + setGetId(e.target.id); + setIsEmail(e.target.value); + }; + + const getPasswordInfo = (e) => { + setGetId(e.target.id); + setIsPassword(e.target.value); + }; + + const emailErrorContext = (txt, flag) => { + setEmailWarnMsg(txt); + setEmailRequiredChk(flag); + }; + + const passwordErrorContext = (txt, flag) => { + setPasswordWarnMsg(txt); + setPasswordRequiredChk(flag); + }; + + const handlePasswordVisible = (e) => { + if (passwordVisible) { + setPasswordVisible(false); + } else { + setPasswordVisible(true); + } + }; + + useEffect(() => { + switch (getId) { + case "useremail": + if (isEmail === "") { + emailErrorContext("이메일을 입력해주세요", true); + setEmailErrorChk(true); + } else if (isEmail !== "" && !EmailValidation(isEmail)) { + emailErrorContext("잘못된 이메일 형식입니다", true); + setEmailErrorChk(true); + } else if (EmailValidation(isEmail)) { + emailErrorContext("", false); + setEmailErrorChk(false); + } + break; + case "userpw": + if (isPassword === "") { + passwordErrorContext("비밀번호를 입력해주세요", true); + setPasswordErrorChk(true); + } else if (isPassword.length < 8) { + passwordErrorContext("비밀번호를 8자 이상 입력해주세요", true); + setPasswordErrorChk(true); + } else if (isPassword.length >= 8) { + passwordErrorContext("", false); + setPasswordErrorChk(false); + } + break; + default: + } + }, [isEmail, isPassword]); + + useEffect(() => { + if (emailRequiredChk !== true && passwordRequiredChk !== true) { + setIsSubmit(true); + } else { + setIsSubmit(false); + } + }, [emailRequiredChk, passwordRequiredChk]); + + return ( + <> +
+
+
+ + 판다마켓 +

판다마켓

+ +
+
+
+
+ +
+ +

{emailWarnMsg}

+
+
+
+ +
+ +

{passwordWarnMsg}

+ +
+
+
+ +
+
+
+
+
+

간편 로그인하기

+
+
+
window.open("https://www.google.com")}> + Google +
+
+
+
+ window.open("https://www.kakaocorp.com/page/") + } + > + Kakao +
+
+
+
+
+
+ 판다마켓이 처음이신가요? 회원가입 +
+
+
+ + ); +} + +export default LoginForm; diff --git a/src/Components/ProductList/BestItem.js b/src/Components/ProductList/BestItem.js index c9c5235a2..5e809c5d8 100644 --- a/src/Components/ProductList/BestItem.js +++ b/src/Components/ProductList/BestItem.js @@ -1,7 +1,7 @@ import { useState } from "react"; -import defaultImg from "../../Assets/images/common/no_img.jpg"; +import defaultImg from "../../Assets/images/app/common/no_img.jpg"; import btnWish from "../../Assets/images/productList/btn_wish.png"; -import styles from "../../Styles/ProductList/common.module.css"; +import styles from "../../Styles/ProductList/ProductList.module.css"; import RecentFilter from "./RecentFilter"; function BestItem({ item }) { @@ -9,8 +9,9 @@ function BestItem({ item }) { const handleImgError = (e) => { e.target.src = defaultImg; }; - const price = item.price; - const priceNum = price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); + const formattedPrice = item.price + .toString() + .replace(/\B(?=(\d{3})+(?!\d))/g, ","); return ( <>
@@ -24,7 +25,7 @@ function BestItem({ item }) {

{item.name}

-

{priceNum}

+

{formattedPrice}

찜하기

{item.favoriteCount}

@@ -42,14 +43,13 @@ function BestItemsList() {

베스트 상품

    - {productList && - productList.map((item) => { - return ( -
  • - -
  • - ); - })} + {productList.map((item) => { + return ( +
  • + +
  • + ); + })}
diff --git a/src/Components/ProductList/GeneralItem.js b/src/Components/ProductList/GeneralItem.js index 86004a346..0d9cd0e2d 100644 --- a/src/Components/ProductList/GeneralItem.js +++ b/src/Components/ProductList/GeneralItem.js @@ -1,19 +1,19 @@ -import defaultImg from "../../Assets/images/common/no_img.jpg"; +import { useState } from "react"; +import defaultImg from "../../Assets/images/app/common/no_img.jpg"; import btnWish from "../../Assets/images/productList/btn_wish.png"; -import styles from "../../Styles/ProductList/common.module.css"; +import styles from "../../Styles/ProductList/ProductList.module.css"; +import notFoundImg from "../../Assets/images/productList/not_found.png"; import ProductSearchForm from "./SearchForm"; -import { useState } from "react"; function GeneralItem({ item }) { const [loaded, setLoaded] = useState(false); const handleImgError = (e) => { e.target.src = defaultImg; }; - const price = item.price; - const priceNum = price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); + const priceNum = item.price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); return ( <> -
+
    - {productList && - productList.map((item) => { + {productContainer.length !== 0 ? ( + productContainer.map((item) => { return (
  • ); - })} + }) + ) : ( +
    + Not Found +

    검색하신 상품을 찾을 수 없습니다

    +
    + )}
); diff --git a/src/Components/ProductList/RecentFilter.js b/src/Components/ProductList/RecentFilter.js index 0bd8ca565..493c8d45e 100644 --- a/src/Components/ProductList/RecentFilter.js +++ b/src/Components/ProductList/RecentFilter.js @@ -1,8 +1,8 @@ -import { useEffect, useState, useLayoutEffect } from "react"; -import productData from "../../api"; +import { useState, useEffect } from "react"; +import getProductData from "../../Api/api"; import { useMediaQuery } from "react-responsive"; -function RecentFilter({ productList, setProductList }) { +function RecentFilter({ setProductList }) { const isTablet = useMediaQuery({ query: "(max-width: 1200px)", }); @@ -17,14 +17,14 @@ function RecentFilter({ productList, setProductList }) { const handleLoad = async (options) => { try { - const { list } = await productData(options); + const { list } = await getProductData(options); setProductList(list); } catch (error) { console.log(error); } }; - useLayoutEffect(() => { + useEffect(() => { const handleResize = () => { setIsResponsive(window.innerWidth); isMobile ? setIsWidth(1) : isTablet ? setIsWidth(2) : setIsWidth(4); @@ -36,7 +36,7 @@ function RecentFilter({ productList, setProductList }) { }; }, [isResponsive]); - useLayoutEffect(() => { + useEffect(() => { handleLoad({ orderBy, pageSize: isWidth, diff --git a/src/Components/ProductList/SearchForm.js b/src/Components/ProductList/SearchForm.js index 139320944..b6b4b69ba 100644 --- a/src/Components/ProductList/SearchForm.js +++ b/src/Components/ProductList/SearchForm.js @@ -1,19 +1,17 @@ import arrowDown from "../../Assets/images/productList/select_down.svg"; import productSearch from "../../Assets/images/productList/pd_search.png"; -import styles from "../../Styles/ProductList/common.module.css"; -import productData from "../../api"; +import styles from "../../Styles/ProductList/ProductList.module.css"; +import getProductData from "../../Api/api"; import { Link } from "react-router-dom"; -import { useState, useLayoutEffect, useEffect } from "react"; +import { useState, useEffect } from "react"; import { useMediaQuery } from "react-responsive"; -import Pagination from "react-js-pagination"; function ProductSearchForm({ - productList, - setProductList, + setProductContainer, page, setPage, - pageCount, setPageCount, + setIsDataCount, }) { const isTablet = useMediaQuery({ query: "(max-width: 1200px)", @@ -27,6 +25,7 @@ function ProductSearchForm({ const [search, setSearch] = useState(""); const [isWidth, setIsWidth] = useState(isMobile ? 4 : isTablet ? 6 : 10); const [isResponsive, setIsResponsive] = useState(window.innerWidth); + setIsDataCount(isWidth); const handleFilterToggle = () => { toggle ? setToggle(false) : setToggle(true); @@ -50,18 +49,21 @@ function ProductSearchForm({ const handleLoad = async (options) => { try { - const { list, totalCount } = await productData(options); - setProductList(list); + const { list, totalCount } = await getProductData(options); + setProductContainer(list); setPageCount(totalCount); } catch (error) { console.log(error); } }; - useLayoutEffect(() => { + useEffect(() => { const handleResize = () => { setIsResponsive(window.innerWidth); isMobile ? setIsWidth(4) : isTablet ? setIsWidth(6) : setIsWidth(10); + // 페이지 창 크기 조절 시 pagination 1로 초기화(추후 불필요하단 판단 시 아래 코드만 삭제) + setPage(1); + setIsDataCount(isWidth); }; window.addEventListener("resize", handleResize); return () => { @@ -70,7 +72,7 @@ function ProductSearchForm({ }; }, [isResponsive]); - useLayoutEffect(() => { + useEffect(() => { handleLoad({ orderBy, pageSize: isWidth, diff --git a/src/Components/ProductRgs/ImgPreview.js b/src/Components/ProductRgs/ImgPreview.js new file mode 100644 index 000000000..c225ffd66 --- /dev/null +++ b/src/Components/ProductRgs/ImgPreview.js @@ -0,0 +1,15 @@ +import styles from "../../Styles/ProductRgs/ProductRgs.module.css"; +import deleteBtnImg from "../../Assets/images/productRgs/cancel.svg"; + +function ImgPreview({ preview, handleDeleteClick }) { + return ( + <> + +
+ 삭제 +
+ + ); +} + +export default ImgPreview; diff --git a/src/Components/ProductRgs/RegisterForm.js b/src/Components/ProductRgs/RegisterForm.js new file mode 100644 index 000000000..028b1f99c --- /dev/null +++ b/src/Components/ProductRgs/RegisterForm.js @@ -0,0 +1,67 @@ +import styles from "../../Styles/ProductRgs/ProductRgs.module.css"; +import { useState, useEffect } from "react"; +import RegisterInput from "./RegisterInput"; +import RegisterImgFile from "./RegisterImgFile"; + +function RegisterForm() { + const INITIAL_VALUES = { + // 이미지 필수 추가 조건을 대비한 코드 + // imgFile: null, + title: "", + description: "", + price: 0, + tag: [], + }; + + // file input 제외한 모든 input + const [values, setValues] = useState(INITIAL_VALUES); + // file input + const [imgFile, setImgFile] = useState(""); + const [isDisableChk, setIsDisableChk] = useState(true); + + const handleSubmit = async (e) => { + e.preventDefault(); + const formData = new FormData(); + formData.append("title", values.title); + formData.append("description", values.description); + formData.append("price", values.price); + }; + + useEffect(() => { + if ( + values.title !== "" && + values.description !== "" && + values.price > 0 && + values.tag.length > 0 + ) { + setIsDisableChk(false); + } else { + setIsDisableChk(true); + } + }, [values]); + + return ( +
+
+
+

상품 등록하기

+ +
+ + + +
+ ); +} + +export default RegisterForm; diff --git a/src/Components/ProductRgs/RegisterImgFile.js b/src/Components/ProductRgs/RegisterImgFile.js new file mode 100644 index 000000000..856510272 --- /dev/null +++ b/src/Components/ProductRgs/RegisterImgFile.js @@ -0,0 +1,87 @@ +import styles from "../../Styles/ProductRgs/ProductRgs.module.css"; +import uploadImg from "../../Assets/images/productRgs/upload.svg"; +import ImgPreview from "./ImgPreview"; +import { useEffect, useRef, useState } from "react"; + +// 상품 이미지 등록 +function RegisterImgFile({ imgFile, setImgFile }) { + const [preview, setPreview] = useState(""); + const [isImgChk, setIsImgChk] = useState(false); + const inputRef = useRef(); + + const handleChange = (e) => { + const nextValue = e.target.files[0]; + // 이미지 필수 추가 조건을 대비한 코드 + // setValues((prevValue) => ({ + // ...prevValue, + // ["imgFile"]: nextValue, + // })); + setImgFile(nextValue); + // 같은 파일을 재업로드 할 경우 event가 trigger되지 않는 버그 방지 + e.target.value = ""; + }; + + const handlePreventionClick = (e) => { + if (imgFile) { + e.preventDefault(); + setIsImgChk(true); + } else { + setIsImgChk(false); + } + }; + + const handleDeleteClick = () => { + setIsImgChk(false); + setImgFile(""); + setPreview(""); + }; + + useEffect(() => { + if (!imgFile) return; + const nextPreview = URL.createObjectURL(imgFile); + setPreview(nextPreview); + }, [imgFile]); + + return ( +
+

상품 이미지

+
+
+ + +
+ {preview && ( +
+ +
+ )} +
+ {isImgChk ? ( +

+ *이미지 등록은 최대 1개까지 가능합니다. +

+ ) : ( + "" + )} +
+ ); +} + +export default RegisterImgFile; diff --git a/src/Components/ProductRgs/RegisterInput.js b/src/Components/ProductRgs/RegisterInput.js new file mode 100644 index 000000000..11781c10a --- /dev/null +++ b/src/Components/ProductRgs/RegisterInput.js @@ -0,0 +1,132 @@ +import { useEffect, useRef, useState } from "react"; +import { NumericFormat } from "react-number-format"; +import styles from "../../Styles/ProductRgs/ProductRgs.module.css"; +import deleteBtnImg from "../../Assets/images/productRgs/cancel.svg"; + +// 상품 정보 등록 +function RegisterInput({ setValues }) { + const [tag, setTag] = useState(""); + const [tagList, setTagList] = useState([]); + const [price, setPrice] = useState(); + const priceInput = useRef(); + + useEffect(() => { + if (priceInput.current.value === "") { + setPrice(0); + } + setValues((prevValue) => ({ + ...prevValue, + ["price"]: price, + })); + }, [price]); + + const handleChange = (name, value) => { + setValues((prevValue) => ({ + ...prevValue, + [name]: value, + })); + }; + + const handleInputChange = (e) => { + let { name, value } = e.target; + handleChange(name, value); + }; + + // onKeyDown 이벤트 키가 Enter와 일치하면 실행 + const activeEnter = (e) => { + const regExp = /[ \{\}\[\]\/?.,;:|\)*~`!^\+┼<>@\#$%&\'\"\\\(\=]/gi; + // onKeyDown 이벤트의 한글 입력 시 이벤트가 두 번 호출 되는 버그 방지 + if (e.nativeEvent.isComposing) { + return; + } + // 특수문자 및 스페이스바 입력 방지 + if (regExp.test(e.key)) { + e.preventDefault(); + } + if (e.key === "Enter") { + if (e.target.value === "") { + // 빈 칸 엔터 방지 + e.preventDefault(); + } else { + handleChange("tag", tagList); + tagList.push(tag); + e.target.value = ""; + } + } + }; + + const handleAddValue = (e) => { + setTag(e.target.value); + }; + + // 클릭한 태그 삭제 + const handleDeleteClick = (i) => { + tagList.splice(i, 1); + handleChange("tag", tagList); + }; + + return ( + <> +
+

상품명

+ +
+
+

상품 소개

+