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 ( -
-
- logo -

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

- - Learn React - -
-
- ); -} - -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 ( +
+

Войти

+
+ + +
+
+ + + {authFailed &&
Неверные имя пользователя или пароль
} +
+ + +
+ ); +}; + +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",