diff --git a/package-lock.json b/package-lock.json
index aa07701..0f9322c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2347,6 +2347,12 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
+ "@types/lodash": {
+ "version": "4.14.173",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.173.tgz",
+ "integrity": "sha512-vv0CAYoaEjCw/mLy96GBTnRoZrSxkGE0BKzKimdR8P3OzrNYNvBgtW7p055A+E8C31vXNUhWKoFCbhq7gbyhFg==",
+ "dev": true
+ },
"@types/mdast": {
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz",
@@ -2410,6 +2416,15 @@
"@types/react": "*"
}
},
+ "@types/react-input-mask": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/react-input-mask/-/react-input-mask-3.0.1.tgz",
+ "integrity": "sha512-rGOW8t2Ac558TAAnaq5ZZDGHu1siTblBeY30ggAQ9sX89y2MX0/FFVDLd2DT85QRpYIQOrb7QzkF9RCoV2+nZg==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*"
+ }
+ },
"@types/react-router": {
"version": "5.1.16",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.16.tgz",
@@ -2432,9 +2447,9 @@
}
},
"@types/react-transition-group": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.3.tgz",
- "integrity": "sha512-fUx5muOWSYP8Bw2BUQ9M9RK9+W1XBK/7FLJ8PTQpnpTEkn0ccyMffyEQvan4C3h53gHdx7KE5Qrxi/LnUGQtdg==",
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.2.tgz",
+ "integrity": "sha512-KibDWL6nshuOJ0fu8ll7QnV/LVTo3PzQ9aCPnRUYPfX7eZohHwLIdNHj7pftanREzHNP4/nJa8oeM73uSiavMQ==",
"requires": {
"@types/react": "*"
}
@@ -3057,6 +3072,14 @@
"postcss-value-parser": "^4.1.0"
}
},
+ "axios": {
+ "version": "0.21.4",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+ "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+ "requires": {
+ "follow-redirects": "^1.14.0"
+ }
+ },
"babel-jest": {
"version": "27.2.0",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.2.0.tgz",
@@ -5565,8 +5588,7 @@
"follow-redirects": {
"version": "1.14.3",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.3.tgz",
- "integrity": "sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw==",
- "dev": true
+ "integrity": "sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw=="
},
"form-data": {
"version": "3.0.1",
@@ -5579,6 +5601,32 @@
"mime-types": "^2.1.12"
}
},
+ "formik": {
+ "version": "2.2.9",
+ "resolved": "https://registry.npmjs.org/formik/-/formik-2.2.9.tgz",
+ "integrity": "sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==",
+ "requires": {
+ "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": "^1.10.0"
+ },
+ "dependencies": {
+ "deepmerge": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
+ "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ }
+ }
+ },
"forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -6345,6 +6393,15 @@
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
"dev": true
},
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
@@ -8545,6 +8602,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
+ "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=="
+ },
"lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
@@ -9082,6 +9144,12 @@
"integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
"dev": true
},
+ "nanoclone": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz",
+ "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==",
+ "dev": true
+ },
"nanoid": {
"version": "3.1.25",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz",
@@ -10223,6 +10291,12 @@
"react-is": "^16.8.1"
}
},
+ "property-expr": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.4.tgz",
+ "integrity": "sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg==",
+ "dev": true
+ },
"proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -10355,6 +10429,21 @@
"scheduler": "^0.20.2"
}
},
+ "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=="
+ },
+ "react-input-mask": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/react-input-mask/-/react-input-mask-2.0.4.tgz",
+ "integrity": "sha512-1hwzMr/aO9tXfiroiVCx5EtKohKwLk/NT8QlJXHQ4N+yJJFyUuMT+zfTpLBwX/lK3PkuMlievIffncpMZ3HGRQ==",
+ "dev": true,
+ "requires": {
+ "invariant": "^2.2.4",
+ "warning": "^4.0.2"
+ }
+ },
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@@ -12011,6 +12100,12 @@
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
"dev": true
},
+ "toposort": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
+ "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=",
+ "dev": true
+ },
"tough-cookie": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
@@ -12407,6 +12502,15 @@
"makeerror": "1.0.x"
}
},
+ "warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"watchpack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz",
@@ -12749,6 +12853,21 @@
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true
},
+ "yup": {
+ "version": "0.32.9",
+ "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.9.tgz",
+ "integrity": "sha512-Ci1qN+i2H0XpY7syDQ0k5zKQ/DoxO0LzPg8PAR/X4Mpj6DqaeCoIYEEjDJwhArh3Fa7GWbQQVDZKeXYlSH4JMg==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.10.5",
+ "@types/lodash": "^4.14.165",
+ "lodash": "^4.17.20",
+ "lodash-es": "^4.17.15",
+ "nanoclone": "^0.2.1",
+ "property-expr": "^2.0.4",
+ "toposort": "^2.0.2"
+ }
+ },
"zwitch": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz",
diff --git a/package.json b/package.json
index cc14832..94b25c0 100644
--- a/package.json
+++ b/package.json
@@ -29,7 +29,9 @@
"dependencies": {
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
+ "axios": "^0.21.4",
"eventemitter3": "^4.0.7",
+ "formik": "^2.2.9",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.3.0",
@@ -50,6 +52,7 @@
"@types/jest": "^26.0.24",
"@types/react": "^17.0.19",
"@types/react-dom": "^17.0.9",
+ "@types/react-input-mask": "^3.0.1",
"@types/react-router-dom": "^5.1.8",
"@typescript-eslint/eslint-plugin": "^4.31.1",
"@typescript-eslint/parser": "^4.31.1",
@@ -74,6 +77,7 @@
"postcss": "^8.3.6",
"postcss-loader": "^6.1.1",
"prettier": "2.3.2",
+ "react-input-mask": "^2.0.4",
"style-loader": "^3.2.1",
"stylelint": "^13.13.1",
"stylelint-config-prettier": "^8.0.2",
@@ -84,7 +88,8 @@
"typescript-eslint": "0.0.1-alpha.0",
"webpack": "^5.52.1",
"webpack-cli": "^4.8.0",
- "webpack-dev-server": "^4.2.0"
+ "webpack-dev-server": "^4.2.0",
+ "yup": "^0.32.9"
},
"browserslist": {
"production": [
diff --git a/public/assets/mario-background.jpeg b/public/assets/mario-background.jpeg
new file mode 100644
index 0000000..257c2a5
Binary files /dev/null and b/public/assets/mario-background.jpeg differ
diff --git a/public/index.html b/public/index.html
index 5e3c952..0ff6cf8 100644
--- a/public/index.html
+++ b/public/index.html
@@ -5,14 +5,8 @@
-
-
+
+
Mario Pro Max
diff --git a/src/App.tsx b/src/App.tsx
index ec95a96..1712dec 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,28 +1,91 @@
/*
* Copyright (c) 2021. Written by Leonid Artemev (me@artemev.it)
*/
-import { createBrowserHistory } from "history";
import React from "react";
+import { createBrowserHistory } from "history";
import { Route, Router, Switch } from "react-router-dom";
-import { Login, Registration, Leaderboard, Forum } from "./pages";
+import { Login, Registration, Leaderboard, Forum, Profile } from "./pages";
+import { createTheme, ThemeProvider } from "@material-ui/core";
+
import { Game } from "./pages/Game";
+declare module "@material-ui/core/styles" {
+ interface Theme {
+ loginPage: {
+ background: string;
+ backdropFilter: string;
+ };
+ }
+ // allow configuration using `createTheme`
+ interface ThemeOptions {
+ loginPage?: {
+ background: string;
+ backdropFilter: string;
+ };
+ }
+}
+
const history = createBrowserHistory();
const StubComponent = () => Under construction! 👻
;
+const darkTheme = createTheme({
+ typography: {
+ fontFamily: [
+ "-apple-system",
+ "BlinkMacSystemFont",
+ '"Segoe UI"',
+ "Roboto",
+ '"Helvetica Neue"',
+ "Arial",
+ "sans-serif",
+ '"Apple Color Emoji"',
+ '"Segoe UI Emoji"',
+ '"Segoe UI Symbol"',
+ ].join(","),
+ },
+ loginPage: {
+ background: "#00000033",
+ backdropFilter: "blur(10px)",
+ },
+});
+
+const lightTheme = createTheme({
+ typography: {
+ fontFamily: [
+ "-apple-system",
+ "BlinkMacSystemFont",
+ '"Segoe UI"',
+ "Roboto",
+ '"Helvetica Neue"',
+ "Arial",
+ "sans-serif",
+ '"Apple Color Emoji"',
+ '"Segoe UI Emoji"',
+ '"Segoe UI Symbol"',
+ ].join(","),
+ },
+ loginPage: {
+ background: "#FFFFFF88",
+ backdropFilter: "blur(3px)",
+ },
+});
+
function App() {
return (
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/src/constants/url.ts b/src/constants/url.ts
new file mode 100644
index 0000000..89a1d6a
--- /dev/null
+++ b/src/constants/url.ts
@@ -0,0 +1,5 @@
+export const API_BASE_URL = "https://ya-praktikum.tech/api/v2";
+export const SIGNIN_URL = `${API_BASE_URL}/auth/signin`;
+export const SIGNUP_URL = `${API_BASE_URL}/auth/signup`;
+export const SIGNOUT_URL = `${API_BASE_URL}/auth/logout`;
+export const USER_URL = `${API_BASE_URL}/auth/user`;
diff --git a/src/constants/validationErrors.ts b/src/constants/validationErrors.ts
new file mode 100644
index 0000000..b2a288f
--- /dev/null
+++ b/src/constants/validationErrors.ts
@@ -0,0 +1,7 @@
+export const ERROR_MESSAGES = {
+ required: "Required field",
+ min: (num: number): string => `Minimum length ${num} symbols`,
+ max: (num: number): string => `Maximum length ${num} symbols`,
+ email: "Invalid email format",
+ password_mismatch: "Passwords do not match",
+};
diff --git a/src/constants/validationSchema.ts b/src/constants/validationSchema.ts
new file mode 100644
index 0000000..b39baf7
--- /dev/null
+++ b/src/constants/validationSchema.ts
@@ -0,0 +1,53 @@
+import { object, ref, SchemaOf, string } from "yup";
+
+import { ERROR_MESSAGES } from "./validationErrors";
+
+import { User as UserModel } from "../services/auth";
+
+const VALIDATION = {
+ login: {
+ min: 3,
+ max: 20,
+ },
+ first_name: {
+ min: 3,
+ max: 20,
+ },
+ second_name: {
+ min: 3,
+ max: 20,
+ },
+ password: {
+ min: 6,
+ max: 20,
+ },
+};
+
+export type User = Pick & { password: string };
+
+export const UserSchema: SchemaOf = object().shape({
+ email: string().email(ERROR_MESSAGES.email).required(ERROR_MESSAGES.required),
+ login: string()
+ .min(VALIDATION.login.min, ERROR_MESSAGES.min(VALIDATION.login.min))
+ .max(VALIDATION.login.max, ERROR_MESSAGES.min(VALIDATION.login.max))
+ .required(ERROR_MESSAGES.required),
+ first_name: string()
+ .min(VALIDATION.login.min, ERROR_MESSAGES.min(VALIDATION.login.min))
+ .max(VALIDATION.login.max, ERROR_MESSAGES.min(VALIDATION.login.max))
+ .required(ERROR_MESSAGES.required),
+ second_name: string()
+ .min(VALIDATION.login.min, ERROR_MESSAGES.min(VALIDATION.login.min))
+ .max(VALIDATION.login.max, ERROR_MESSAGES.min(VALIDATION.login.max))
+ .required(ERROR_MESSAGES.required),
+ phone: string()
+ .required(ERROR_MESSAGES.required)
+ .transform((value: string) => value.replace(/\D/g, ""))
+ .min(11, ERROR_MESSAGES.required),
+ password: string()
+ .min(VALIDATION.password.min, ERROR_MESSAGES.min(VALIDATION.password.min))
+ .max(VALIDATION.password.max, ERROR_MESSAGES.min(VALIDATION.password.max))
+ .required(ERROR_MESSAGES.required),
+ password_confirm: string()
+ .oneOf([ref("password"), undefined], ERROR_MESSAGES.password_mismatch)
+ .required(ERROR_MESSAGES.required),
+});
diff --git a/src/index.css b/src/index.css
index 79aa911..2db3492 100644
--- a/src/index.css
+++ b/src/index.css
@@ -4,14 +4,21 @@
body {
margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
- "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
- sans-serif;
+ 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;
+ font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
+}
+
+@keyframes from-left-to-right {
+ from {
+ transform: translateX(-100%);
+ }
+ to {
+ transform: translateX(0);
+ }
}
diff --git a/src/pages/Login/Login.tsx b/src/pages/Login/Login.tsx
index bb15889..38b942a 100644
--- a/src/pages/Login/Login.tsx
+++ b/src/pages/Login/Login.tsx
@@ -1,7 +1,129 @@
-import React from "react";
+import React, { useEffect } from "react";
+import Button from "@material-ui/core/Button";
+import CssBaseline from "@material-ui/core/CssBaseline";
+import TextField from "@material-ui/core/TextField";
+import Link from "@material-ui/core/Link";
+import Box from "@material-ui/core/Box";
+import Grid from "@material-ui/core/Grid";
+import Typography from "@material-ui/core/Typography";
+import { makeStyles } from "@material-ui/core/styles";
+import { Slide } from "@material-ui/core";
+import { getUser, signin, SigninProps } from "../../services/auth";
+import { UserSchema } from "../../constants/validationSchema";
+import { SchemaOf } from "yup";
+import { useFormik } from "formik";
+import { useHistory } from "react-router";
-function Login() {
- return ;
-}
+import { Footer } from "../../components/Footer";
+
+const useStyles = makeStyles((theme) => ({
+ main: {
+ height: "100vh",
+ backgroundImage: "url(/assets/mario-background.jpeg)",
+ backgroundRepeat: "no-repeat",
+ backgroundSize: "cover",
+ backgroundPosition: "left",
+ animationName: "from-left-to-right",
+ animationDuration: "4s",
+ },
+
+ formContainer: {
+ background: theme.loginPage.background,
+ backdropFilter: theme.loginPage.backdropFilter,
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center",
+ flexDirection: "column",
+ },
+}));
+
+export const signInSchema = UserSchema.pick(["login", "password"]) as SchemaOf;
+
+const initialValues: SigninProps = {
+ login: "",
+ password: "",
+};
-export default Login;
+export default function Login() {
+ const classes = useStyles();
+ const history = useHistory();
+
+ const formik = useFormik({
+ initialValues,
+ validationSchema: signInSchema,
+ onSubmit: (values) => {
+ signin(values).then((data) => {
+ history.push("/profile");
+ });
+ },
+ });
+
+ useEffect(() => {
+ getUser().then(() => history.push("/profile"));
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+ Sign in
+
+
+
+
+
+ {"Don't have an account? Sign Up"}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/Profile/Profile.test.tsx b/src/pages/Profile/Profile.test.tsx
new file mode 100644
index 0000000..9149a6a
--- /dev/null
+++ b/src/pages/Profile/Profile.test.tsx
@@ -0,0 +1,9 @@
+import * as React from "react";
+import * as ReactDOM from "react-dom";
+import { Profile } from ".";
+
+it("renders without crashing", () => {
+ const div = document.createElement("div");
+ ReactDOM.render(, div);
+ ReactDOM.unmountComponentAtNode(div);
+});
diff --git a/src/pages/Profile/Profile.tsx b/src/pages/Profile/Profile.tsx
new file mode 100644
index 0000000..376fdad
--- /dev/null
+++ b/src/pages/Profile/Profile.tsx
@@ -0,0 +1,36 @@
+import React, { useEffect, useState, useCallback } from "react";
+import { getUser, signout } from "../../services/auth";
+import { User } from "../../services/auth";
+import TextField from "@material-ui/core/TextField";
+import { useHistory } from "react-router";
+
+export default function Profile() {
+ const history = useHistory();
+
+ const [user, setUser] = useState();
+
+ const updateUser = useCallback(() => {
+ getUser().then((user) => setUser(user));
+ }, []);
+
+ const signOut = useCallback(() => {
+ signout().then(() => history.push("/login"));
+ }, []);
+
+ useEffect(updateUser, []);
+
+ if (!user) {
+ return <>Not Logged in>;
+ }
+
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/Profile/index.ts b/src/pages/Profile/index.ts
new file mode 100644
index 0000000..d3a49de
--- /dev/null
+++ b/src/pages/Profile/index.ts
@@ -0,0 +1 @@
+export { default as Profile } from "./Profile";
diff --git a/src/pages/Registration/Registration.tsx b/src/pages/Registration/Registration.tsx
index 62a1c06..662d60b 100644
--- a/src/pages/Registration/Registration.tsx
+++ b/src/pages/Registration/Registration.tsx
@@ -1,7 +1,219 @@
-import React from "react";
+import React, { useEffect } from "react";
+import Button from "@material-ui/core/Button";
+import CssBaseline from "@material-ui/core/CssBaseline";
+import TextField from "@material-ui/core/TextField";
+import Link from "@material-ui/core/Link";
+import Box from "@material-ui/core/Box";
+import Grid from "@material-ui/core/Grid";
+import Typography from "@material-ui/core/Typography";
+import { makeStyles } from "@material-ui/core/styles";
+import { Slide } from "@material-ui/core";
+import { useFormik } from "formik";
+import { SchemaOf } from "yup";
+import InputMask from "react-input-mask";
+import { Footer } from "../../components/Footer";
-function Registration() {
- return ;
-}
+import { UserSchema } from "../../constants/validationSchema";
+import { getUser, signup, SignupProps } from "../../services/auth";
+import { useHistory } from "react-router";
+
+const useStyles = makeStyles((theme) => ({
+ main: {
+ height: "100vh",
+ backgroundImage: "url(/assets/mario-background.jpeg)",
+ backgroundRepeat: "no-repeat",
+ backgroundSize: "cover",
+ backgroundPosition: "left",
+ animationName: "from-left-to-right",
+ animationDuration: "4s",
+ },
+
+ formContainer: {
+ background: theme.loginPage.background,
+ backdropFilter: theme.loginPage.backdropFilter,
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center",
+ flexDirection: "column",
+ },
+}));
+
+export const signUpSchema = UserSchema.pick([
+ "email",
+ "login",
+ "first_name",
+ "second_name",
+ "phone",
+ "password",
+ "password_confirm",
+]) as SchemaOf;
+
+const initialValues: SignupProps = {
+ email: "",
+ login: "",
+ first_name: "",
+ second_name: "",
+ phone: "",
+ password: "",
+ password_confirm: "",
+};
+
+export default function Registration() {
+ const classes = useStyles();
+ const history = useHistory();
-export default Registration;
+ const formik = useFormik({
+ initialValues,
+ validationSchema: signUpSchema,
+ onSubmit: (values) => {
+ signup(values).then((data) => {
+ history.push("/profile");
+ });
+ },
+ });
+
+ useEffect(() => {
+ getUser().then(() => history.push("/profile"));
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+ Sign up
+
+
+
+
+
+
+ {"Have an account? Sign In"}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/index.ts b/src/pages/index.ts
index 255083c..03814a9 100644
--- a/src/pages/index.ts
+++ b/src/pages/index.ts
@@ -2,3 +2,4 @@ export { Login } from "./Login";
export { Registration } from "./Registration";
export { Leaderboard } from "./Leaderboard";
export { Forum } from "./Forum";
+export { Profile } from "./Profile";
diff --git a/src/services/auth.ts b/src/services/auth.ts
new file mode 100644
index 0000000..0e9effc
--- /dev/null
+++ b/src/services/auth.ts
@@ -0,0 +1,46 @@
+import axios from "axios";
+
+import { SIGNIN_URL, SIGNOUT_URL, SIGNUP_URL, USER_URL } from "../constants/url";
+
+axios.defaults.withCredentials = true;
+
+export interface User {
+ id: number;
+ login: string;
+ first_name: string;
+ second_name: string;
+ display_name: string;
+ email: string;
+ phone: string;
+ avatar: string;
+}
+
+export type SigninProps = Pick & { password: string };
+
+export type SignupProps = Pick & {
+ password: string;
+ password_confirm: string;
+};
+
+export interface SignupResponse {
+ id: number;
+}
+
+export const getUser = (cookies?: string): Promise => {
+ const options = {
+ withCredentials: true,
+ headers: {},
+ };
+ if (cookies) {
+ options.headers = {
+ Cookie: cookies,
+ };
+ }
+ return axios.get(USER_URL, options).then((response) => response.data as User);
+};
+
+export const signin = (data: SigninProps): Promise => axios.post(SIGNIN_URL, data);
+
+export const signup = (data: SignupProps): Promise => axios.post(SIGNUP_URL, data);
+
+export const signout = (): Promise => axios.post(SIGNOUT_URL, { withCredentials: true });