From 566d8be8d9e244545195e679b5dcfc5c05ccb696 Mon Sep 17 00:00:00 2001 From: jhynsoo Date: Mon, 16 Oct 2023 22:51:40 +0900 Subject: [PATCH] feat: Change layout, add pages --- .eslintrc.cjs | 4 +- package-lock.json | 128 +++++++++++++++++++-- package.json | 5 +- src/API.js | 39 +++++++ src/App.jsx | 13 ++- src/Router.jsx | 80 +++++++++++++ src/assets/logo.svg | 1 + src/assets/phone.svg | 1 + src/components/Brand/BrandProductList.jsx | 19 +++ src/components/Brand/BrandTitle.jsx | 15 +++ src/components/Button/index.jsx | 27 +++++ src/components/ButtonArea/index.jsx | 11 ++ src/components/CSRFToken/CSRFToken.jsx | 13 +++ src/components/CheckBox/index.jsx | 21 ++++ src/components/CurrencyInput/index.jsx | 36 ++++++ src/components/Graph/ProductPriceGraph.jsx | 86 ++++++++++++++ src/components/ImageViewer/index.jsx | 33 ++++++ src/components/Layout/AuthMenu.jsx | 24 ++++ src/components/Layout/Footer.jsx | 13 +++ src/components/Layout/Header.jsx | 28 +++++ src/components/Layout/HeaderLinks.jsx | 22 ++++ src/components/Layout/Nav.jsx | 5 + src/components/Layout/PageLayout.jsx | 24 ++++ src/components/Loading/index.jsx | 10 ++ src/components/Login/LoginForm.jsx | 72 ++++++++++++ src/components/PanelLayout/index.jsx | 15 +++ src/components/Post/PostList.jsx | 19 +++ src/components/PostItem/index.jsx | 25 ++++ src/components/ProductItem/index.jsx | 14 +++ src/components/ProductList/index.jsx | 19 +++ src/components/ProductPostList/index.jsx | 19 +++ src/components/Signup/SignupForm.jsx | 89 ++++++++++++++ src/components/Title/index.jsx | 7 ++ src/components/Write/BrandModelSelect.jsx | 56 +++++++++ src/components/Write/BrandSelect.jsx | 41 +++++++ src/components/Write/ModelSelect.jsx | 42 +++++++ src/components/Write/PriceInput.jsx | 30 +++++ src/components/Write/TextInput.jsx | 47 ++++++++ src/components/Write/WriteForm.jsx | 45 ++++++++ src/constants.js | 0 src/hooks/network/brand.js | 41 +++++++ src/hooks/network/graph.js | 15 +++ src/hooks/network/isLogged.js | 9 ++ src/hooks/network/post.js | 29 +++++ src/hooks/network/product.js | 46 ++++++++ src/methods/startViewTransition.js | 5 + src/pages/Brand/index.jsx | 16 +++ src/pages/Login/index.jsx | 24 ++++ src/pages/Logout/index.jsx | 29 +++++ src/pages/MyPage/index.jsx | 26 +++++ src/pages/NotFound/index.jsx | 9 ++ src/pages/PostDetail/index.jsx | 46 ++++++++ src/pages/Posts/index.jsx | 15 +++ src/pages/ProductDetail/index.jsx | 36 ++++++ src/pages/Products/index.jsx | 13 +++ src/pages/Signup/index.jsx | 13 +++ src/pages/Write/index.jsx | 14 +++ src/pages/WriteSteps/PhotoStep.jsx | 38 ++++++ src/pages/WriteSteps/PriceStep.jsx | 33 ++++++ src/pages/WriteSteps/ProductSelectStep.jsx | 45 ++++++++ src/pages/WriteSteps/StatusStep.jsx | 70 +++++++++++ src/pages/WriteSteps/TextStep.jsx | 40 +++++++ src/pages/WriteSteps/index.jsx | 85 ++++++++++++++ src/styles/Brand.styles.js | 27 +++++ src/styles/Button.styles.js | 32 ++++++ src/styles/ButtonArea.styles.js | 19 +++ src/styles/CheckBox.styles.js | 46 ++++++++ src/styles/Common.styles.js | 19 +++ src/styles/CurrencyInput.styles.js | 30 +++++ src/styles/Form.styles.js | 79 +++++++++++++ src/styles/Global.js | 23 ++++ src/styles/ImageViewer.style.js | 29 +++++ src/styles/KeyFrames.styles.js | 12 ++ src/styles/Layout.styles.js | 92 +++++++++++++++ src/styles/MyPage.styles.js | 32 ++++++ src/styles/PanelLayout.styles.js | 34 ++++++ src/styles/PostDetail.styles.js | 42 +++++++ src/styles/PostItem.styles.js | 50 ++++++++ src/styles/Posts.styles.js | 5 + src/styles/ProductPriceGraph.styles.js | 26 +++++ src/styles/Products.styles.js | 16 +++ src/styles/Themes.styles.js | 55 +++++++++ src/styles/WriteSteps.styles.js | 65 +++++++++++ 83 files changed, 2614 insertions(+), 14 deletions(-) create mode 100644 src/API.js create mode 100644 src/Router.jsx create mode 100644 src/assets/logo.svg create mode 100644 src/assets/phone.svg create mode 100644 src/components/Brand/BrandProductList.jsx create mode 100644 src/components/Brand/BrandTitle.jsx create mode 100644 src/components/Button/index.jsx create mode 100644 src/components/ButtonArea/index.jsx create mode 100644 src/components/CSRFToken/CSRFToken.jsx create mode 100644 src/components/CheckBox/index.jsx create mode 100644 src/components/CurrencyInput/index.jsx create mode 100644 src/components/Graph/ProductPriceGraph.jsx create mode 100644 src/components/ImageViewer/index.jsx create mode 100644 src/components/Layout/AuthMenu.jsx create mode 100644 src/components/Layout/Footer.jsx create mode 100644 src/components/Layout/Header.jsx create mode 100644 src/components/Layout/HeaderLinks.jsx create mode 100644 src/components/Layout/Nav.jsx create mode 100644 src/components/Layout/PageLayout.jsx create mode 100644 src/components/Loading/index.jsx create mode 100644 src/components/Login/LoginForm.jsx create mode 100644 src/components/PanelLayout/index.jsx create mode 100644 src/components/Post/PostList.jsx create mode 100644 src/components/PostItem/index.jsx create mode 100644 src/components/ProductItem/index.jsx create mode 100644 src/components/ProductList/index.jsx create mode 100644 src/components/ProductPostList/index.jsx create mode 100644 src/components/Signup/SignupForm.jsx create mode 100644 src/components/Title/index.jsx create mode 100644 src/components/Write/BrandModelSelect.jsx create mode 100644 src/components/Write/BrandSelect.jsx create mode 100644 src/components/Write/ModelSelect.jsx create mode 100644 src/components/Write/PriceInput.jsx create mode 100644 src/components/Write/TextInput.jsx create mode 100644 src/components/Write/WriteForm.jsx create mode 100644 src/constants.js create mode 100644 src/hooks/network/brand.js create mode 100644 src/hooks/network/graph.js create mode 100644 src/hooks/network/isLogged.js create mode 100644 src/hooks/network/post.js create mode 100644 src/hooks/network/product.js create mode 100644 src/methods/startViewTransition.js create mode 100644 src/pages/Brand/index.jsx create mode 100644 src/pages/Login/index.jsx create mode 100644 src/pages/Logout/index.jsx create mode 100644 src/pages/MyPage/index.jsx create mode 100644 src/pages/NotFound/index.jsx create mode 100644 src/pages/PostDetail/index.jsx create mode 100644 src/pages/Posts/index.jsx create mode 100644 src/pages/ProductDetail/index.jsx create mode 100644 src/pages/Products/index.jsx create mode 100644 src/pages/Signup/index.jsx create mode 100644 src/pages/Write/index.jsx create mode 100644 src/pages/WriteSteps/PhotoStep.jsx create mode 100644 src/pages/WriteSteps/PriceStep.jsx create mode 100644 src/pages/WriteSteps/ProductSelectStep.jsx create mode 100644 src/pages/WriteSteps/StatusStep.jsx create mode 100644 src/pages/WriteSteps/TextStep.jsx create mode 100644 src/pages/WriteSteps/index.jsx create mode 100644 src/styles/Brand.styles.js create mode 100644 src/styles/Button.styles.js create mode 100644 src/styles/ButtonArea.styles.js create mode 100644 src/styles/CheckBox.styles.js create mode 100644 src/styles/Common.styles.js create mode 100644 src/styles/CurrencyInput.styles.js create mode 100644 src/styles/Form.styles.js create mode 100644 src/styles/Global.js create mode 100644 src/styles/ImageViewer.style.js create mode 100644 src/styles/KeyFrames.styles.js create mode 100644 src/styles/Layout.styles.js create mode 100644 src/styles/MyPage.styles.js create mode 100644 src/styles/PanelLayout.styles.js create mode 100644 src/styles/PostDetail.styles.js create mode 100644 src/styles/PostItem.styles.js create mode 100644 src/styles/Posts.styles.js create mode 100644 src/styles/ProductPriceGraph.styles.js create mode 100644 src/styles/Products.styles.js create mode 100644 src/styles/Themes.styles.js create mode 100644 src/styles/WriteSteps.styles.js diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 4dcb439..d94d95f 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -16,5 +16,7 @@ module.exports = { 'warn', { allowConstantExport: true }, ], + 'react/jsx-props-no-spreading': 'off', + 'react/prop-types': 'off', }, -} +}; diff --git a/package-lock.json b/package-lock.json index 587ad9f..2034cde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,10 +12,13 @@ "axios": "^1.5.0", "chart.js": "^4.4.0", "react": "^18.2.0", + "react-chartjs-2": "^5.2.0", + "react-cookie": "^6.1.1", "react-dom": "^18.2.0", "react-query": "^3.39.3", "react-router-dom": "^6.16.0", - "styled-components": "^6.0.8" + "styled-components": "^6.0.8", + "zustand": "^4.4.1" }, "devDependencies": { "@types/react": "^18.2.15", @@ -2241,6 +2244,17 @@ "integrity": "sha512-xVRaR4u9hcYjFvcSg71Lz5Bo4//CyjAAfMxa7UsaDSYxAshflUkVJWiyVWrfxC59z2kP1IzI4/1BEpnhI9o3Mw==", "dev": true }, + "node_modules/@swc/helpers": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", + "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@swc/types": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", @@ -2267,17 +2281,29 @@ "date-fns": "^2.25.0" } }, + "node_modules/@types/cookie": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.2.tgz", + "integrity": "sha512-DBpRoJGKJZn7RY92dPrgoMew8xCWc2P71beqsjyhEI/Ds9mOyVmBwtekyfhpwFIVt1WrxTonFifiOZ62V8CnNA==" + }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-YIQtIg4PKr7ZyqNPZObpxfHsHEmuB8dXCxd6qVcGuQVDK2bpsF7bYNnBJ4Nn7giuACZg+WewExgrtAJ3XnA4Xw==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/prop-types": { "version": "15.7.7", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.7.tgz", - "integrity": "sha512-FbtmBWCcSa2J4zL781Zf1p5YUBXQomPEcep9QZCfRfQgTxz3pJWiDFLebohZ9fFntX5ibzOkSsrJ0TEew8cAog==", - "dev": true + "integrity": "sha512-FbtmBWCcSa2J4zL781Zf1p5YUBXQomPEcep9QZCfRfQgTxz3pJWiDFLebohZ9fFntX5ibzOkSsrJ0TEew8cAog==" }, "node_modules/@types/react": { "version": "18.2.22", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.22.tgz", "integrity": "sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==", - "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -2296,8 +2322,7 @@ "node_modules/@types/scheduler": { "version": "0.16.4", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.4.tgz", - "integrity": "sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==", - "dev": true + "integrity": "sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==" }, "node_modules/@types/stylis": { "version": "4.2.1", @@ -2821,6 +2846,14 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/core-js-compat": { "version": "3.32.2", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.32.2.tgz", @@ -3724,6 +3757,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -4629,9 +4670,9 @@ } }, "node_modules/postcss": { - "version": "8.4.30", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz", - "integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -4725,6 +4766,28 @@ "node": ">=0.10.0" } }, + "node_modules/react-chartjs-2": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", + "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==", + "peerDependencies": { + "chart.js": "^4.1.1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-cookie": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-6.1.1.tgz", + "integrity": "sha512-fuFRpf8LH6SfmVMowDUIRywJF5jAUDUWrm0EI5VdXfTl5bPcJ7B0zWbuYpT0Tvikx7Gs18MlvAT+P+744dUz2g==", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.1", + "hoist-non-react-statics": "^3.3.2", + "universal-cookie": "^6.0.0" + }, + "peerDependencies": { + "react": ">= 16.3.0" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -4740,8 +4803,7 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-query": { "version": "3.39.3", @@ -5454,6 +5516,15 @@ "node": ">=4" } }, + "node_modules/universal-cookie": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-6.1.1.tgz", + "integrity": "sha512-33S9x3CpdUnnjwTNs2Fgc41WGve2tdLtvaK2kPSbZRc5pGpz2vQFbRWMxlATsxNNe/Cy8SzmnmbuBM85jpZPtA==", + "dependencies": { + "@types/cookie": "^0.5.1", + "cookie": "^0.5.0" + } + }, "node_modules/unload": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz", @@ -5501,6 +5572,14 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/vite": { "version": "4.4.9", "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", @@ -5668,6 +5747,33 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz", + "integrity": "sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 021d122..ef20bc2 100644 --- a/package.json +++ b/package.json @@ -14,10 +14,13 @@ "axios": "^1.5.0", "chart.js": "^4.4.0", "react": "^18.2.0", + "react-chartjs-2": "^5.2.0", + "react-cookie": "^6.1.1", "react-dom": "^18.2.0", "react-query": "^3.39.3", "react-router-dom": "^6.16.0", - "styled-components": "^6.0.8" + "styled-components": "^6.0.8", + "zustand": "^4.4.1" }, "devDependencies": { "@types/react": "^18.2.15", diff --git a/src/API.js b/src/API.js new file mode 100644 index 0000000..a89b32c --- /dev/null +++ b/src/API.js @@ -0,0 +1,39 @@ +import axios from 'axios'; +import { Cookies } from 'react-cookie'; + +export const API = axios.create({ + baseURL: import.meta.env.VITE_SERVER_URL ?? '/', + withCredentials: true, + header: { + 'Content-Type': 'application/json', + cache: 'no-cache', + accept: 'application/json', + referrer: 'no-referrer', + }, + xsrfCookieName: 'csrftoken', + xsrfHeaderName: 'x-csrftoken', + timeout: 1000 * 60 * 5, +}); + +const cookies = new Cookies(); + +export const setCookie = (name, value, options) => { + return cookies.set(name, value, options); +}; + +export const getCookie = (name) => { + return cookies.get(name); +}; + +export const URL = { + login: '/user/login/', + logout: '/user/logout/', + myPage: '/user/mypage/', + user: '/user/', + brand: '/brand/', + product: '/product/', + post: '/post/', + write: '/write', + write2: '/newPost', + graph: '/graph/g', +}; diff --git a/src/App.jsx b/src/App.jsx index 5a265f7..0a93d57 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,9 +1,20 @@ import { QueryClient, QueryClientProvider } from 'react-query'; +import Router from './Router'; +import GlobalStyle from './styles/global'; +import { ThemeProvider } from 'styled-components'; +import theme from './styles/Themes.styles'; const queryClient = new QueryClient(); const App = () => { - return ; + return ( + + + + + + + ); }; export default App; diff --git a/src/Router.jsx b/src/Router.jsx new file mode 100644 index 0000000..31c5996 --- /dev/null +++ b/src/Router.jsx @@ -0,0 +1,80 @@ +import { BrowserRouter, Route, Routes } from 'react-router-dom'; +import PageLayout from './components/Layout/PageLayout'; +import NotFound from './pages/NotFound'; +import Products from './pages/Products'; +import ProductDetail from './pages/ProductDetail'; +import { URL } from './API'; +import Brand from './pages/Brand'; +import Posts from './pages/Posts'; +import PostDetail from './pages/PostDetail'; +import Write from './pages/Write'; +import Login from './pages/Login'; +import Logout from './pages/Logout'; +import Signup from './pages/Signup'; +import WriteSteps from './pages/WriteSteps'; +import CurrencyInput from './components/CurrencyInput'; +import { useState } from 'react'; +import MyPage from './pages/MyPage'; + +const Router = () => ( + + + }> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + + } + /> + + +); + +export default Router; diff --git a/src/assets/logo.svg b/src/assets/logo.svg new file mode 100644 index 0000000..ece354c --- /dev/null +++ b/src/assets/logo.svg @@ -0,0 +1 @@ + diff --git a/src/assets/phone.svg b/src/assets/phone.svg new file mode 100644 index 0000000..b6a4b8a --- /dev/null +++ b/src/assets/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Brand/BrandProductList.jsx b/src/components/Brand/BrandProductList.jsx new file mode 100644 index 0000000..3fa360b --- /dev/null +++ b/src/components/Brand/BrandProductList.jsx @@ -0,0 +1,19 @@ +import { useBrandProduct } from '../../hooks/network/brand'; +import ProductItem from '../ProductItem'; + +const BrandProductList = ({ id }) => { + const { data } = useBrandProduct(id); + + return ( + <> + {data?.map((product) => ( + + ))} + + ); +}; + +export default BrandProductList; diff --git a/src/components/Brand/BrandTitle.jsx b/src/components/Brand/BrandTitle.jsx new file mode 100644 index 0000000..d800fa9 --- /dev/null +++ b/src/components/Brand/BrandTitle.jsx @@ -0,0 +1,15 @@ +import { Suspense } from 'react'; +import { useBrand } from '../../hooks/network/brand'; +import { Title } from '../../styles/Common.styles'; + +const BrandTitle = ({ id }) => { + const { data } = useBrand(id); + + return ( + + <Suspense fallback={null}>{data?.name}</Suspense> + + ); +}; + +export default BrandTitle; diff --git a/src/components/Button/index.jsx b/src/components/Button/index.jsx new file mode 100644 index 0000000..cf52861 --- /dev/null +++ b/src/components/Button/index.jsx @@ -0,0 +1,27 @@ +import * as S from '../../styles/Button.styles'; + +function Button({ + children, + onClick, + className, + disabled, + secondary, + type = 'button', + ...rest +}) { + const ButtonComponent = secondary ? S.Button.secondary : S.Button.primary; + + return ( + + {children} + + ); +} + +export default Button; diff --git a/src/components/ButtonArea/index.jsx b/src/components/ButtonArea/index.jsx new file mode 100644 index 0000000..a74b78a --- /dev/null +++ b/src/components/ButtonArea/index.jsx @@ -0,0 +1,11 @@ +import * as S from '../../styles/ButtonArea.styles'; + +function ButtonArea({ children }) { + return ( + + {children} + + ); +} + +export default ButtonArea; diff --git a/src/components/CSRFToken/CSRFToken.jsx b/src/components/CSRFToken/CSRFToken.jsx new file mode 100644 index 0000000..acf07dc --- /dev/null +++ b/src/components/CSRFToken/CSRFToken.jsx @@ -0,0 +1,13 @@ +import { getCookie } from '../../API'; + +const CSRFToken = () => { + return ( + + ); +}; + +export default CSRFToken; diff --git a/src/components/CheckBox/index.jsx b/src/components/CheckBox/index.jsx new file mode 100644 index 0000000..c02e10f --- /dev/null +++ b/src/components/CheckBox/index.jsx @@ -0,0 +1,21 @@ +import * as S from '../../styles/CheckBox.styles'; + +function CheckBox({ name, id, text, checked, setChecked }) { + return ( + + setChecked(!checked)} + /> + + {name} + {text} + + + ); +} + +export default CheckBox; diff --git a/src/components/CurrencyInput/index.jsx b/src/components/CurrencyInput/index.jsx new file mode 100644 index 0000000..adcfcdf --- /dev/null +++ b/src/components/CurrencyInput/index.jsx @@ -0,0 +1,36 @@ +import * as S from '../../styles/CurrencyInput.styles'; + +function CurrencyInput({ value, setValue }) { + const getRenderValue = () => { + if (value === '') { + return ''; + } + const numberValue = Number(value); + return `${numberValue.toLocaleString()}원`; + }; + const handleOnChange = (event) => { + if (event.nativeEvent.inputType === 'deleteContentBackward') { + const newValue = value.slice(0, -1); + newValue === 0 ? setValue('') : setValue(newValue); + return; + } + const { value: newValue } = event.target; + const onlyNumber = newValue.replace(/[^0-9]/g, ''); + if (onlyNumber > 9999999) { + return; + } + onlyNumber === '0' ? setValue('') : setValue(onlyNumber); + }; + + return ( + + + + ); +} + +export default CurrencyInput; diff --git a/src/components/Graph/ProductPriceGraph.jsx b/src/components/Graph/ProductPriceGraph.jsx new file mode 100644 index 0000000..1953093 --- /dev/null +++ b/src/components/Graph/ProductPriceGraph.jsx @@ -0,0 +1,86 @@ +import * as S from '../../styles/ProductPriceGraph.styles'; +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + PointElement, + LineElement, + // Title, + Tooltip, + // Legend, +} from 'chart.js'; +import { Line } from 'react-chartjs-2'; +import { useGraph } from '../../hooks/network/graph'; +import theme from '../../styles/Themes.styles'; + +ChartJS.register( + CategoryScale, + LinearScale, + PointElement, + LineElement, + // Title, + Tooltip +); + +const options = { + maintainAspectRatio: false, + responsive: true, + scales: { + y: { + ticks: { + stepSize: 50000, + }, + display: false, + }, + }, + plugins: { + tooltip: { + padding: 10, + backgroundColor: 'white', + titleColor: theme.colors.gray900, + bodyColor: theme.colors.gray900, + borderWidth: 1, + borderColor: theme.colors.primary, + callbacks: { + label: (context) => `${context.dataset.data[context.dataIndex]}원`, + }, + }, + }, +}; + +const ProductPriceGraph = ({ id }) => { + const { data: graphData } = useGraph(id); + const primaryColor = theme.colors.primary; + const graphStyleData = { + labels: graphData?.map((data) => `${data.year}.${data.month}`), + datasets: [ + { + label: '가격', + data: graphData?.map((data) => data.price), + backgroundColor: primaryColor, + borderColor: primaryColor, + cubicInterpolationMode: 'monotone', + }, + ], + }; + const minPrice = graphData?.reduce((min, data) => { + if (min > data.price) { + return data.price; + } + return min; + }, graphData[0]?.price); + + return ( + + + + + 최근 6개월 최저가: {minPrice}원 + + ); +}; + +export default ProductPriceGraph; diff --git a/src/components/ImageViewer/index.jsx b/src/components/ImageViewer/index.jsx new file mode 100644 index 0000000..74734e7 --- /dev/null +++ b/src/components/ImageViewer/index.jsx @@ -0,0 +1,33 @@ +import { useState } from 'react'; +import * as S from '../../styles/ImageViewer.style'; +import startViewTransition from '../../methods/startViewTransition'; +import { SubTitle } from '../../styles/Common.styles'; + +function ImageViewer({ images, title }) { + const [currentImage, setCurrentImage] = useState(images[0]); + const handleClick = (image) => () => { + startViewTransition(() => setCurrentImage(image)); + }; + + return ( + + {title && {title}} + + + {images.map((image) => ( + 이미지 + ))} + + + ); +} + +export default ImageViewer; diff --git a/src/components/Layout/AuthMenu.jsx b/src/components/Layout/AuthMenu.jsx new file mode 100644 index 0000000..4bd5df5 --- /dev/null +++ b/src/components/Layout/AuthMenu.jsx @@ -0,0 +1,24 @@ +import * as S from '../../styles/Layout.styles'; +import { URL } from '../../API'; +import useIsLogged from '../../hooks/network/isLogged'; + +const AuthMenu = () => { + const isLogged = useIsLogged((state) => state.isLogged); + + if (!isLogged) { + return ( +
+ 로그인 +
+ ); + } + + return ( +
+ 글쓰기 + 마이페이지 +
+ ); +}; + +export default AuthMenu; diff --git a/src/components/Layout/Footer.jsx b/src/components/Layout/Footer.jsx new file mode 100644 index 0000000..3693c75 --- /dev/null +++ b/src/components/Layout/Footer.jsx @@ -0,0 +1,13 @@ +import * as S from '../../styles/Layout.styles'; + +const Footer = () => { + return ( + + +

© {new Date().getFullYear()} App. All rights reserved.

+
+
+ ); +}; + +export default Footer; diff --git a/src/components/Layout/Header.jsx b/src/components/Layout/Header.jsx new file mode 100644 index 0000000..e7be5ec --- /dev/null +++ b/src/components/Layout/Header.jsx @@ -0,0 +1,28 @@ +import * as S from '../../styles/Layout.styles'; +import { URL } from '../../API'; +import LogoImage from '../../assets/logo.svg'; +import AuthMenu from './AuthMenu'; + +function Header() { + return ( + +
+ + + + +
+ 기기 목록 + 최신글 +
+ +
+
+
+ ); +} + +export default Header; diff --git a/src/components/Layout/HeaderLinks.jsx b/src/components/Layout/HeaderLinks.jsx new file mode 100644 index 0000000..9aa10d1 --- /dev/null +++ b/src/components/Layout/HeaderLinks.jsx @@ -0,0 +1,22 @@ +import * as S from '../../styles/Layout.styles'; +import { URL } from '../../API'; +import { useBrands } from '../../hooks/network/brand'; + +const HeaderLinks = () => { + const { data } = useBrands(); + + return ( + <> + {data?.map((brand) => ( + +
{brand?.name}
+
+ ))} + + ); +}; + +export default HeaderLinks; diff --git a/src/components/Layout/Nav.jsx b/src/components/Layout/Nav.jsx new file mode 100644 index 0000000..de17769 --- /dev/null +++ b/src/components/Layout/Nav.jsx @@ -0,0 +1,5 @@ +function Nav() { + return ( ); +} + +export default Nav; diff --git a/src/components/Layout/PageLayout.jsx b/src/components/Layout/PageLayout.jsx new file mode 100644 index 0000000..0d3fcba --- /dev/null +++ b/src/components/Layout/PageLayout.jsx @@ -0,0 +1,24 @@ +import * as S from '../../styles/Layout.styles'; +import { Outlet } from 'react-router'; +import Header from './Header'; +import Footer from './Footer'; +import { ErrorBoundary } from '@toss/error-boundary'; +import { Suspense } from 'react'; + +const PageLayout = () => { + return ( + <> +
+ + <>}> + }> + + + + +