diff --git a/frontend/.eslintrc.yml b/frontend/.eslintrc.yml
index cf98365..14b0cef 100644
--- a/frontend/.eslintrc.yml
+++ b/frontend/.eslintrc.yml
@@ -27,6 +27,7 @@ rules:
functional/no-conditional-statement: 0
functional/no-conditional-statements: 0
functional/no-expression-statement: 0
+ functional/no-expression-statements: 0
functional/immutable-data: 0
functional/functional-parameters: 0
functional/no-try-statement: 0
@@ -37,3 +38,4 @@ rules:
[2, { "namedComponents": "arrow-function" }]
testing-library/no-debug: 0
react/jsx-filename-extension: [1, { "extensions": [".js", ".jsx"] }]
+ jsx-a11y/label-has-associated-control: 0
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 519d241..fd644b5 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -11,10 +11,16 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
+ "axios": "^1.6.2",
+ "bootstrap": "^5.3.2",
+ "classnames": "^2.3.2",
+ "formik": "^2.4.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-router-dom": "^6.20.0",
"react-scripts": "5.0.1",
- "web-vitals": "^2.1.4"
+ "web-vitals": "^2.1.4",
+ "yup": "^1.3.2"
},
"devDependencies": {
"eslint": "^8.53.0",
@@ -3270,6 +3276,24 @@
}
}
},
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "peer": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
+ "node_modules/@remix-run/router": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.13.0.tgz",
+ "integrity": "sha512-5dMOnVnefRsl4uRnAdoWjtVTdh8e6aZqgM4puy9nmEADH72ck+uXwzpJLEKE9Q6F8ZljNewLgmTfkxUrBdv4WA==",
+ "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",
@@ -4035,6 +4059,15 @@
"@types/node": "*"
}
},
+ "node_modules/@types/hoist-non-react-statics": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz",
+ "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==",
+ "dependencies": {
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0"
+ }
+ },
"node_modules/@types/html-minifier-terser": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
@@ -5330,6 +5363,29 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
+ "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
+ "dependencies": {
+ "follow-redirects": "^1.15.0",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/axios/node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/axobject-query": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
@@ -5735,6 +5791,24 @@
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
},
+ "node_modules/bootstrap": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.2.tgz",
+ "integrity": "sha512-D32nmNWiQHo94BKHLmOrdjlL05q1c8oxbtBphQFb9Z5to6eGRDCm0QgeaZ4zFBHzfg2++rqa2JkqCcxDy0sH0g==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/twbs"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/bootstrap"
+ }
+ ],
+ "peerDependencies": {
+ "@popperjs/core": "^2.11.8"
+ }
+ },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -6000,6 +6074,11 @@
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz",
"integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ=="
},
+ "node_modules/classnames": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
+ "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
+ },
"node_modules/clean-css": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz",
@@ -8746,6 +8825,38 @@
"node": ">= 6"
}
},
+ "node_modules/formik": {
+ "version": "2.4.5",
+ "resolved": "https://registry.npmjs.org/formik/-/formik-2.4.5.tgz",
+ "integrity": "sha512-Gxlht0TD3vVdzMDHwkiNZqJ7Mvg77xQNfmBRrNtvzcHZs72TJppSTDKHpImCMJZwcWPBJ8jSQQ95GJzXFf1nAQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://opencollective.com/formik"
+ }
+ ],
+ "dependencies": {
+ "@types/hoist-non-react-statics": "^3.3.1",
+ "deepmerge": "^2.1.1",
+ "hoist-non-react-statics": "^3.3.0",
+ "lodash": "^4.17.21",
+ "lodash-es": "^4.17.21",
+ "react-fast-compare": "^2.0.1",
+ "tiny-warning": "^1.0.2",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/formik/node_modules/deepmerge": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
+ "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -9150,6 +9261,19 @@
"he": "bin/he"
}
},
+ "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/hoist-non-react-statics/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=="
+ },
"node_modules/hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@@ -12492,6 +12616,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
+ "node_modules/lodash-es": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+ },
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@@ -14759,6 +14888,11 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
+ "node_modules/property-expr": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
+ "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA=="
+ },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -14779,6 +14913,11 @@
"node": ">= 0.10"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@@ -15062,6 +15201,11 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
},
+ "node_modules/react-fast-compare": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
+ "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
+ },
"node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@@ -15075,6 +15219,36 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-router": {
+ "version": "6.20.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.20.0.tgz",
+ "integrity": "sha512-pVvzsSsgUxxtuNfTHC4IxjATs10UaAtvLGVSA1tbUE4GDaOSU1Esu2xF5nWLz7KPiMuW8BJWuPFdlGYJ7/rW0w==",
+ "dependencies": {
+ "@remix-run/router": "1.13.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.20.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.20.0.tgz",
+ "integrity": "sha512-CbcKjEyiSVpA6UtCHOIYLUYn/UJfwzp55va4yEfpk7JBN3GPqWfHrdLkAvNCcpXr8QoihcDMuk0dzWZxtlB/mQ==",
+ "dependencies": {
+ "@remix-run/router": "1.13.0",
+ "react-router": "6.20.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",
@@ -16821,6 +16995,16 @@
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
},
+ "node_modules/tiny-case": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
+ "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q=="
+ },
+ "node_modules/tiny-warning": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+ },
"node_modules/tmpl": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@@ -16853,6 +17037,11 @@
"node": ">=0.6"
}
},
+ "node_modules/toposort": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
+ "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="
+ },
"node_modules/tough-cookie": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
@@ -18267,6 +18456,28 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/yup": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/yup/-/yup-1.3.2.tgz",
+ "integrity": "sha512-6KCM971iQtJ+/KUaHdrhVr2LDkfhBtFPRnsG1P8F4q3uUVQ2RfEM9xekpha9aA4GXWJevjM10eDcPQ1FfWlmaQ==",
+ "dependencies": {
+ "property-expr": "^2.0.5",
+ "tiny-case": "^1.0.3",
+ "toposort": "^2.0.2",
+ "type-fest": "^2.19.0"
+ }
+ },
+ "node_modules/yup/node_modules/type-fest": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
}
}
}
diff --git a/frontend/package.json b/frontend/package.json
index 44f68f3..bf1321d 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -7,10 +7,16 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
+ "axios": "^1.6.2",
+ "bootstrap": "^5.3.2",
+ "classnames": "^2.3.2",
+ "formik": "^2.4.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-router-dom": "^6.20.0",
"react-scripts": "5.0.1",
- "web-vitals": "^2.1.4"
+ "web-vitals": "^2.1.4",
+ "yup": "^1.3.2"
},
"scripts": {
"start": "react-scripts start",
diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico
deleted file mode 100644
index a11777c..0000000
Binary files a/frontend/public/favicon.ico and /dev/null differ
diff --git a/frontend/public/index.html b/frontend/public/index.html
index aa069f2..3371499 100644
--- a/frontend/public/index.html
+++ b/frontend/public/index.html
@@ -1,43 +1,18 @@
-
+
-
-
-
-
-
React App
-
-
-
-
+
+
-
+
\ No newline at end of file
diff --git a/frontend/public/logo192.png b/frontend/public/logo192.png
deleted file mode 100644
index fc44b0a..0000000
Binary files a/frontend/public/logo192.png and /dev/null differ
diff --git a/frontend/public/logo512.png b/frontend/public/logo512.png
deleted file mode 100644
index a4e47a6..0000000
Binary files a/frontend/public/logo512.png and /dev/null differ
diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json
deleted file mode 100644
index 080d6c7..0000000
--- a/frontend/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/frontend/public/robots.txt b/frontend/public/robots.txt
deleted file mode 100644
index e9e57dc..0000000
--- a/frontend/public/robots.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-# https://www.robotstxt.org/robotstxt.html
-User-agent: *
-Disallow:
diff --git a/frontend/src/App.css b/frontend/src/App.css
deleted file mode 100644
index 74b5e05..0000000
--- a/frontend/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/frontend/src/App.js b/frontend/src/App.js
deleted file mode 100644
index 3784575..0000000
--- a/frontend/src/App.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import logo from './logo.svg';
-import './App.css';
-
-function App() {
- return (
-
- );
-}
-
-export default App;
diff --git a/frontend/src/App.test.js b/frontend/src/App.test.js
deleted file mode 100644
index 1f03afe..0000000
--- a/frontend/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/frontend/src/Components/App.js b/frontend/src/Components/App.js
new file mode 100644
index 0000000..0420327
--- /dev/null
+++ b/frontend/src/Components/App.js
@@ -0,0 +1,23 @@
+import { BrowserRouter, Routes, Route } from 'react-router-dom';
+import getRoutes from '../routes.js';
+import Layout from './Layout.js';
+import NotfoundPage from '../page/NotfoundPage.js';
+import LoginPage from '../page/LoginPage.js';
+import AuthProvider from '../hoc/AuthProvider.js';
+
+const App = () => (
+
+
+
+
+ }>
+ } />
+ } />
+
+
+
+
+
+);
+
+export default App;
diff --git a/frontend/src/Components/Layout.js b/frontend/src/Components/Layout.js
new file mode 100644
index 0000000..73948b0
--- /dev/null
+++ b/frontend/src/Components/Layout.js
@@ -0,0 +1,25 @@
+import {
+ Link, Outlet, useLocation, Navigate,
+} from 'react-router-dom';
+import getRoutes from '../routes';
+import useAuth from '../hook/useAuth';
+
+const Layout = () => {
+ const location = useLocation();
+ const { userToken } = useAuth();
+ if (location.pathname === getRoutes.main() && !userToken) {
+ return ;
+ }
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default Layout;
diff --git a/frontend/src/Components/LoginForm.js b/frontend/src/Components/LoginForm.js
new file mode 100644
index 0000000..c2eb979
--- /dev/null
+++ b/frontend/src/Components/LoginForm.js
@@ -0,0 +1,92 @@
+import { useFormik } from 'formik';
+import axios from 'axios';
+import cn from 'classnames';
+import {
+ useContext, useEffect, useRef, useState,
+} from 'react';
+import { useLocation, useNavigate } from 'react-router-dom';
+import { AuthContext } from '../hoc/AuthProvider';
+import getRoutes from '../routes.js';
+
+const LoginForm = () => {
+ const [authFailed, setAuthFailed] = useState(false);
+ const { signIn } = useContext(AuthContext);
+ const inputRef = useRef();
+ const location = useLocation();
+ const navigate = useNavigate();
+
+ const fromPage = location.state?.from?.pathname ?? getRoutes.main();
+
+ const formik = useFormik({
+ initialValues: {
+ username: '',
+ password: '',
+ },
+ onSubmit: async (values) => {
+ try {
+ setAuthFailed(false);
+ const response = (await axios.post('/api/v1/login', values));
+ console.log(response);
+ const { token } = response.data;
+ signIn(token, () => navigate(fromPage, { replace: true }));
+ } catch (err) {
+ if (err.isAxiosError && err.response.status === 401) {
+ setAuthFailed(true);
+ inputRef.current.select();
+ }
+ }
+ },
+ });
+
+ useEffect(() => {
+ inputRef.current.select();
+ }, []);
+
+ return (
+
+ );
+};
+
+export default LoginForm;
diff --git a/frontend/src/assets/application.scss b/frontend/src/assets/application.scss
new file mode 100644
index 0000000..27af558
--- /dev/null
+++ b/frontend/src/assets/application.scss
@@ -0,0 +1,2 @@
+@import '~bootstrap/scss/bootstrap';
+@import '~react-toastify/dist/ReactToastify.css';
diff --git a/frontend/src/assets/avatar.jpg b/frontend/src/assets/avatar.jpg
new file mode 100644
index 0000000..902ff57
Binary files /dev/null and b/frontend/src/assets/avatar.jpg differ
diff --git a/frontend/src/assets/avatar_1.jpg b/frontend/src/assets/avatar_1.jpg
new file mode 100644
index 0000000..7e3e653
Binary files /dev/null and b/frontend/src/assets/avatar_1.jpg differ
diff --git a/frontend/src/assets/notFound.png b/frontend/src/assets/notFound.png
new file mode 100644
index 0000000..0ff70c7
Binary files /dev/null and b/frontend/src/assets/notFound.png differ
diff --git a/frontend/src/hoc/AuthProvider.js b/frontend/src/hoc/AuthProvider.js
new file mode 100644
index 0000000..c6d90cc
--- /dev/null
+++ b/frontend/src/hoc/AuthProvider.js
@@ -0,0 +1,29 @@
+import { createContext, useMemo, useState } from 'react';
+
+export const AuthContext = createContext(null);
+
+const AuthProvider = ({ children }) => {
+ const [userToken, setUserToken] = useState(localStorage.getItem('userToken') || null);
+
+ const signIn = (newToken, moveLocation) => {
+ setUserToken(newToken);
+ localStorage.setItem('userToken', newToken);
+ moveLocation();
+ };
+
+ const signOut = (moveLocation) => {
+ setUserToken(null);
+ localStorage.removeItem('userToken');
+ moveLocation();
+ };
+
+ const value = useMemo(() => ({
+ userToken,
+ signIn,
+ signOut,
+ }), [userToken]);
+
+ return {children};
+};
+
+export default AuthProvider;
diff --git a/frontend/src/hook/useAuth.js b/frontend/src/hook/useAuth.js
new file mode 100644
index 0000000..c32fffc
--- /dev/null
+++ b/frontend/src/hook/useAuth.js
@@ -0,0 +1,4 @@
+import { useContext } from 'react';
+import { AuthContext } from '../hoc/AuthProvider';
+
+export default () => useContext(AuthContext);
diff --git a/frontend/src/index.css b/frontend/src/index.css
deleted file mode 100644
index ec2585e..0000000
--- a/frontend/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/frontend/src/index.js b/frontend/src/index.js
index d563c0f..a58bf85 100644
--- a/frontend/src/index.js
+++ b/frontend/src/index.js
@@ -1,17 +1,10 @@
-import React from 'react';
+import 'bootstrap/dist/css/bootstrap.min.css';
import ReactDOM from 'react-dom/client';
-import './index.css';
-import App from './App';
-import reportWebVitals from './reportWebVitals';
+import App from './Components/App';
-const root = ReactDOM.createRoot(document.getElementById('root'));
-root.render(
-
-
-
-);
+const app = () => {
+ const root = ReactDOM.createRoot(document.getElementById('chat'));
+ 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();
+app();
diff --git a/frontend/src/logo.svg b/frontend/src/logo.svg
deleted file mode 100644
index 9dfc1c0..0000000
--- a/frontend/src/logo.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/frontend/src/page/LoginPage.js b/frontend/src/page/LoginPage.js
new file mode 100644
index 0000000..2393c8b
--- /dev/null
+++ b/frontend/src/page/LoginPage.js
@@ -0,0 +1,29 @@
+import { Link } from 'react-router-dom';
+import getRoutes from '../routes.js';
+import avatar from '../assets/avatar.jpg';
+import LoginForm from '../Components/LoginForm.js';
+
+const LoginPage = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+ Нет аккаунта?
+ Регистрация
+
+
+
+
+
+
+);
+
+export default LoginPage;
diff --git a/frontend/src/page/NotfoundPage.js b/frontend/src/page/NotfoundPage.js
new file mode 100644
index 0000000..3e5d5a7
--- /dev/null
+++ b/frontend/src/page/NotfoundPage.js
@@ -0,0 +1,21 @@
+import { Link } from 'react-router-dom';
+import notFoundPNG from '../assets/notFound.png';
+import getRoutes from '../routes.js';
+
+const Notfoundpage = () => (
+
+
+
Страница не найдена
+
+ Но вы можете перейти
+ {' '}
+ на главную страницу
+
+
+);
+
+export default Notfoundpage;
diff --git a/frontend/src/reportWebVitals.js b/frontend/src/reportWebVitals.js
deleted file mode 100644
index 5253d3a..0000000
--- a/frontend/src/reportWebVitals.js
+++ /dev/null
@@ -1,13 +0,0 @@
-const reportWebVitals = onPerfEntry => {
- if (onPerfEntry && onPerfEntry instanceof Function) {
- import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
- getCLS(onPerfEntry);
- getFID(onPerfEntry);
- getFCP(onPerfEntry);
- getLCP(onPerfEntry);
- getTTFB(onPerfEntry);
- });
- }
-};
-
-export default reportWebVitals;
diff --git a/frontend/src/routes.js b/frontend/src/routes.js
new file mode 100644
index 0000000..e2cb7da
--- /dev/null
+++ b/frontend/src/routes.js
@@ -0,0 +1,5 @@
+export default {
+ main: () => '/',
+ login: () => '/login',
+ signup: () => '/api/v1/login',
+};
diff --git a/frontend/src/setupTests.js b/frontend/src/setupTests.js
deleted file mode 100644
index 8f2609b..0000000
--- a/frontend/src/setupTests.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// jest-dom adds custom jest matchers for asserting on DOM nodes.
-// allows you to do things like:
-// expect(element).toHaveTextContent(/react/i)
-// learn more: https://github.com/testing-library/jest-dom
-import '@testing-library/jest-dom';
diff --git a/package-lock.json b/package-lock.json
index f7f780c..8c70cc1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,9 @@
"license": "ISC",
"dependencies": {
"@hexlet/chat-server": "^1.1.8"
+ },
+ "engines": {
+ "node": "20.5.1"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -22,9 +25,9 @@
}
},
"node_modules/@babel/runtime": {
- "version": "7.23.2",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz",
- "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==",
+ "version": "7.23.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.4.tgz",
+ "integrity": "sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==",
"peer": true,
"dependencies": {
"regenerator-runtime": "^0.14.0"
@@ -81,9 +84,9 @@
}
},
"node_modules/@eslint/js": {
- "version": "8.53.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz",
- "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==",
+ "version": "8.54.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz",
+ "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==",
"peer": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -146,13 +149,13 @@
}
},
"node_modules/@fastify/jwt": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/@fastify/jwt/-/jwt-7.2.3.tgz",
- "integrity": "sha512-UPHS7E6tmDWMYspq9znKZ54NwN0F+vcQ9e8okkl4iE+bDMaWCoPChL/lUmKLFepU6/Van3grjshmNviQ8oLVwA==",
+ "version": "7.2.4",
+ "resolved": "https://registry.npmjs.org/@fastify/jwt/-/jwt-7.2.4.tgz",
+ "integrity": "sha512-aWJzVb3iZb9xIPjfut8YOrkNEKrZA9xyF2C2Hv9nTheFp7CQPGIZMNTyf3848BsD27nw0JLk8jVLZ2g2DfJOoQ==",
"dependencies": {
"@fastify/error": "^3.0.0",
"@lukeed/ms": "^2.0.0",
- "fast-jwt": "^3.0.0",
+ "fast-jwt": "^3.3.2",
"fastify-plugin": "^4.0.0",
"steed": "^1.1.3"
}
@@ -290,9 +293,9 @@
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
},
"node_modules/@types/cors": {
- "version": "2.8.16",
- "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.16.tgz",
- "integrity": "sha512-Trx5or1Nyg1Fq138PCuWqoApzvoSLWzZ25ORBiHMbbUT42g578lH1GT4TwYDbiUOLFuDsCkfLneT2105fsFWGg==",
+ "version": "2.8.17",
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
+ "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
"dependencies": {
"@types/node": "*"
}
@@ -304,9 +307,9 @@
"peer": true
},
"node_modules/@types/node": {
- "version": "20.9.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz",
- "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==",
+ "version": "20.10.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.0.tgz",
+ "integrity": "sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==",
"dependencies": {
"undici-types": "~5.26.4"
}
@@ -1216,15 +1219,15 @@
}
},
"node_modules/eslint": {
- "version": "8.53.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz",
- "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==",
+ "version": "8.54.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz",
+ "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.3",
- "@eslint/js": "8.53.0",
+ "@eslint/js": "8.54.0",
"@humanwhocodes/config-array": "^0.11.13",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
@@ -1671,9 +1674,9 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"node_modules/fast-jwt": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/fast-jwt/-/fast-jwt-3.3.1.tgz",
- "integrity": "sha512-1YuuIJeh1hEvfcYDe89P2oGACWI5hd2GadRDKHalSxkc1Z0z8I6yzuVK6SF15sW09QZngTV6d7g4+TFL9bvs5A==",
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/fast-jwt/-/fast-jwt-3.3.2.tgz",
+ "integrity": "sha512-H+JYxaFy2LepiC1AQWM/2hzKlQOWaWUkEnu/yebhYu4+ameb3qG77WiRZ1Ct6YBk6d/ESsNguBfTT5+q0XMtKg==",
"dependencies": {
"@lukeed/ms": "^2.0.1",
"asn1.js": "^5.4.1",
@@ -2247,9 +2250,9 @@
]
},
"node_modules/ignore": {
- "version": "5.2.4",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
- "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
+ "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
"peer": true,
"engines": {
"node": ">= 4"
@@ -3208,9 +3211,9 @@
}
},
"node_modules/pino": {
- "version": "8.16.1",
- "resolved": "https://registry.npmjs.org/pino/-/pino-8.16.1.tgz",
- "integrity": "sha512-3bKsVhBmgPjGV9pyn4fO/8RtoVDR8ssW1ev819FsRXlRNgW8gR/9Kx+gCK4UPWd4JjrRDLWpzd/pb1AyWm3MGA==",
+ "version": "8.16.2",
+ "resolved": "https://registry.npmjs.org/pino/-/pino-8.16.2.tgz",
+ "integrity": "sha512-2advCDGVEvkKu9TTVSa/kWW7Z3htI/sBKEZpqiHk6ive0i/7f5b1rsU8jn0aimxqfnSz5bj/nOYkwhBUn5xxvg==",
"dependencies": {
"atomic-sleep": "^1.0.0",
"fast-redact": "^3.1.1",
@@ -3389,9 +3392,9 @@
}
},
"node_modules/process-warning": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.0.tgz",
- "integrity": "sha512-N6mp1+2jpQr3oCFMz6SeHRGbv6Slb20bRhj4v3xR99HqNToAcOe1MFOp4tytyzOfJn+QtN8Rf7U/h2KAn4kC6g=="
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.1.tgz",
+ "integrity": "sha512-JjBvFEn7MwFbzUDa2SRtKJSsyO0LlER4V/FmwLMhBlXNbGgGxdyFCxIdMDLerWUycsVUyaoM9QFLvppFy4IWaQ=="
},
"node_modules/prop-types": {
"version": "15.8.1",