Browse Source

first commit

unknown 2 years ago
commit
191db2e922
49 changed files with 43137 additions and 0 deletions
  1. 1 0
      .eslintcache
  2. 26 0
      .gitignore
  3. 5 0
      .huskyrc
  4. 4 0
      .lintstagedrc
  5. 11 0
      .prettierrc.yaml
  6. 1 0
      README.md
  7. 15 0
      css-draft.css
  8. 40979 0
      package-lock.json
  9. 80 0
      package.json
  10. BIN
      public/favicon.ico
  11. 1 0
      public/icons8-telegram-app.svg
  12. 43 0
      public/index.html
  13. BIN
      public/logo192.png
  14. BIN
      public/logo512.png
  15. 25 0
      public/manifest.json
  16. 3 0
      public/robots.txt
  17. 5 0
      src/App.module.css
  18. 54 0
      src/App.tsx
  19. 62 0
      src/api-data/index.ts
  20. 946 0
      src/components/AuthPage/Authorization/countries.tsx
  21. 166 0
      src/components/AuthPage/Authorization/index.tsx
  22. 76 0
      src/components/AuthPage/QRCode/index.tsx
  23. 111 0
      src/components/AuthPage/Registration/index.tsx
  24. 79 0
      src/components/AuthPage/SMSCode/index.tsx
  25. 72 0
      src/components/AuthPage/index.tsx
  26. 18 0
      src/components/Loader/Loader.jsx
  27. 11 0
      src/components/Loader/Loader.module.css
  28. 18 0
      src/components/Routes/PrivateRoute/PrivateRoute.tsx
  29. 23 0
      src/components/Routes/PublicRoute/PublicRoute.tsx
  30. 1 0
      src/img/icons8-telegram-app.svg
  31. BIN
      src/img/monkey.png
  32. BIN
      src/img/qrCode_telegram.png
  33. BIN
      src/img/telegram.png
  34. 54 0
      src/index.css
  35. 22 0
      src/index.tsx
  36. 1 0
      src/react-app-env.d.ts
  37. 24 0
      src/redux/authorization/action/index.ts
  38. 42 0
      src/redux/authorization/operations/index.ts
  39. 36 0
      src/redux/authorization/reducer/index.ts
  40. 11 0
      src/redux/authorization/selector/index.ts
  41. 7 0
      src/redux/loading/action/index.ts
  42. 7 0
      src/redux/loading/reducer/index.ts
  43. 4 0
      src/redux/loading/selector/index.ts
  44. 19 0
      src/redux/rootReducer/index.ts
  45. 14 0
      src/redux/store/index.ts
  46. 15 0
      src/typescript/components/Routes/interfaces.ts
  47. 13 0
      src/typescript/redux/authorization/interfaces.ts
  48. 6 0
      src/typescript/redux/interfaces.ts
  49. 26 0
      tsconfig.json

File diff suppressed because it is too large
+ 1 - 0
.eslintcache


+ 26 - 0
.gitignore

@@ -0,0 +1,26 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+# /build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Local Netlify folder
+.netlify

+ 5 - 0
.huskyrc

@@ -0,0 +1,5 @@
+{
+  "hooks": {
+    "pre-commit": "lint-staged"
+  }
+}

+ 4 - 0
.lintstagedrc

@@ -0,0 +1,4 @@
+{
+  "src/**/*.{json,css,scss,md}": ["prettier --write"],
+  "src/**/*.{js,jsx,ts,tsx}": ["prettier --write", "eslint --fix"]
+}

+ 11 - 0
.prettierrc.yaml

@@ -0,0 +1,11 @@
+printWidth: 80
+tabWidth: 2
+useTabs: false
+semi: true
+singleQuote: true
+trailingComma: all
+bracketSpacing: true
+jsxBracketSameLine: false
+arrowParens: avoid
+proseWrap: always
+

+ 1 - 0
README.md

@@ -0,0 +1 @@
+just

+ 15 - 0
css-draft.css

@@ -0,0 +1,15 @@
+/* Container */
+
+/* PaintingList */
+.PaintingList {
+  padding: 0;
+  margin: 0;
+  list-style: none;
+  max-width: 1170px;
+  display: flex;
+  margin: -10px auto;
+}
+
+.PaintingList__item {
+  margin: 10px;
+}

File diff suppressed because it is too large
+ 40979 - 0
package-lock.json


+ 80 - 0
package.json

@@ -0,0 +1,80 @@
+{
+  "homepage": "https://web.telegram.org/",
+  "name": "react-21-22",
+  "version": "0.1.0",
+  "private": true,
+  "dependencies": {
+    "@emotion/react": "^11.7.1",
+    "@emotion/styled": "^11.6.0",
+    "@material-ui/core": "^4.11.3",
+    "@mui/icons-material": "^5.3.1",
+    "@mui/material": "^5.4.0",
+    "@reduxjs/toolkit": "^1.5.0",
+    "@testing-library/jest-dom": "^5.11.6",
+    "@testing-library/react": "^11.2.2",
+    "@testing-library/user-event": "^12.2.2",
+    "@types/jest": "^26.0.15",
+    "@types/node": "^14.14.9",
+    "@types/react": "^17.0.0",
+    "@types/react-dom": "^17.0.9",
+    "@types/yup": "^0.29.13",
+    "axios": "^0.21.1",
+    "bootstrap": "^4.6.0",
+    "canvas": "^2.8.0",
+    "formik": "^2.2.6",
+    "gh-pages": "^3.1.0",
+    "modern-normalize": "^1.0.0",
+    "react": "^17.0.1",
+    "react-dom": "^17.0.1",
+    "react-js-pagination": "^3.0.3",
+    "react-loader-spinner": "^4.0.0",
+    "react-query": "^3.24.3",
+    "react-redux": "^7.2.2",
+    "react-router-dom": "^5.2.0",
+    "react-scripts": "4.0.1",
+    "react-toastify": "^6.2.0",
+    "redux": "^4.0.5",
+    "redux-devtools-extension": "^2.13.8",
+    "redux-logger": "^3.0.6",
+    "redux-persist": "^6.0.0",
+    "redux-toolkit": "^1.1.2",
+    "typescript": "^4.3.5",
+    "uuid": "^8.3.1",
+    "web-vitals": "^0.2.4",
+    "yup": "^0.32.8"
+  },
+  "scripts": {
+    "start": "set PORT=3007 && react-scripts start",
+    "build": "react-scripts build",
+    "test": "react-scripts test",
+    "eject": "react-scripts eject",
+    "predeploy": "npm run build",
+    "deploy": "gh-pages -d build"
+  },
+  "eslintConfig": {
+    "extends": [
+      "react-app",
+      "react-app/jest"
+    ]
+  },
+  "browserslist": {
+    "production": [
+      ">0.2%",
+      "not dead",
+      "not op_mini all"
+    ],
+    "development": [
+      "last 1 chrome version",
+      "last 1 firefox version",
+      "last 1 safari version"
+    ]
+  },
+  "devDependencies": {
+    "@types/react-redux": "^7.1.18",
+    "@types/react-router-dom": "^5.1.8",
+    "husky": "^4.3.0",
+    "lint-staged": "^10.5.2",
+    "prettier": "^2.2.1",
+    "prop-types": "^15.7.2"
+  }
+}

BIN
public/favicon.ico


File diff suppressed because it is too large
+ 1 - 0
public/icons8-telegram-app.svg


+ 43 - 0
public/index.html

@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <link rel="icon" href="%PUBLIC_URL%/icons8-telegram-app.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <meta name="theme-color" content="#000000" />
+    <meta
+      name="description"
+      content="Web site created using create-react-app"
+    />
+    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
+    <!--
+      manifest.json provides metadata used when your web app is installed on a
+      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
+    -->
+    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
+    <!--
+      Notice the use of %PUBLIC_URL% in the tags above.
+      It will be replaced with the URL of the `public` folder during the build.
+      Only files inside the `public` folder can be referenced from the HTML.
+
+      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
+      work correctly both with client-side routing and a non-root public URL.
+      Learn how to configure a non-root public URL by running `npm run build`.
+    -->
+    <title>Telegram</title>
+  </head>
+  <body>
+    <noscript>You need to enable JavaScript to run this app.</noscript>
+    <div id="root"></div>
+    <!--
+      This HTML file is a template.
+      If you open it directly in the browser, you will see an empty page.
+
+      You can add webfonts, meta tags, or analytics to this file.
+      The build step will place the bundled scripts into the <body> tag.
+
+      To begin the development, run `npm start` or `yarn start`.
+      To create a production bundle, use `npm run build` or `yarn build`.
+    -->
+  </body>
+</html>

BIN
public/logo192.png


BIN
public/logo512.png


+ 25 - 0
public/manifest.json

@@ -0,0 +1,25 @@
+{
+  "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"
+}

+ 3 - 0
public/robots.txt

@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:

+ 5 - 0
src/App.module.css

@@ -0,0 +1,5 @@
+.appWrapper {
+  min-width: 100vw;
+  min-height: 100vh;
+  background-color: rgb(255, 255, 255);
+}

+ 54 - 0
src/App.tsx

@@ -0,0 +1,54 @@
+import { lazy, Suspense } from 'react';
+import { BrowserRouter, Switch } from 'react-router-dom';
+import { ToastContainer } from 'react-toastify';
+
+import s from './App.module.css';
+import PrivateRoute from './components/Routes/PrivateRoute/PrivateRoute';
+import PublicRoute from './components/Routes/PublicRoute/PublicRoute';
+import Loader from './components/Loader/Loader';
+
+
+
+const AuthPage = lazy(
+  () =>
+    import(
+      './components/AuthPage' /* webpackChunkName: "AuthPage" */
+    ),
+);
+
+
+
+function App() {
+
+  return (
+    <div className={s.appWrapper}>
+      <Suspense fallback={<Loader />}>
+        <BrowserRouter>
+          <Switch>
+            <PrivateRoute exact path="/">
+              Home
+            </PrivateRoute>
+              <PublicRoute exact path={'/z/'} restricted>
+                <AuthPage />
+              </PublicRoute>
+          </Switch>
+        </BrowserRouter>
+      </Suspense>
+      
+      <ToastContainer
+        position="top-right"
+        autoClose={3000}
+        hideProgressBar={false}
+        newestOnTop={false}
+        closeOnClick
+        rtl={false}
+        pauseOnFocusLoss
+        draggable
+        pauseOnHover
+      />
+    </div>
+  );
+}
+
+
+export default App;

+ 62 - 0
src/api-data/index.ts

@@ -0,0 +1,62 @@
+import { toast } from 'react-toastify';
+const axios = require('axios');
+axios.defaults.baseURL = 'http://localhost:3000';
+
+const error = (message:string) =>
+  toast.error(`🔥 ${message}!`, {
+    position: 'bottom-left',
+    autoClose: 3000,
+    hideProgressBar: false,
+    closeOnClick: true,
+    pauseOnHover: true,
+    draggable: true,
+    progress: undefined,
+  });
+
+const success = (message:string) =>
+  toast.success(`⚡ ${message}`, {
+    position: 'bottom-left',
+    autoClose: 3000,
+    hideProgressBar: false,
+    closeOnClick: true,
+    pauseOnHover: true,
+    draggable: true,
+    progress: undefined,
+  });
+
+const setToken = {
+  set(token:string) {
+    axios.defaults.headers.common.Authorization = `Bearer ${token}`;
+  },
+  unset() {
+    axios.defaults.headers.common.Authorization = '';
+  },
+};
+
+const authorizeUser = async (number:string,country:string):Promise<string | undefined> => {
+  try {
+    const { data : {data} } = await axios.post('/auth/register', { number,country });
+    success(`check ${number}`);
+    return data
+  } catch (e) {
+    error('bad request');
+  }
+};
+
+const loginUser = async <T>(number:string,code:string):Promise<T | undefined> => {
+  try {
+    const { data : {data} } = await axios.post('/auth/login', { number,code });
+    success(`AUTHORIZED`);
+    return data
+  } catch (e) {
+    error('UNAUTHORIZED');
+  }
+};
+
+
+
+export {
+  setToken,
+  authorizeUser,
+  loginUser,
+};

+ 946 - 0
src/components/AuthPage/Authorization/countries.tsx

@@ -0,0 +1,946 @@
+export const countries =  [
+    {
+      "code": "+7 840",
+      "name": "Abkhazia"
+    },
+    {
+      "code": "+93",
+      "name": "Afghanistan"
+    },
+    {
+      "code": "+355",
+      "name": "Albania"
+    },
+    {
+      "code": "+213",
+      "name": "Algeria"
+    },
+    {
+      "code": "+1 684",
+      "name": "American Samoa"
+    },
+    {
+      "code": "+376",
+      "name": "Andorra"
+    },
+    {
+      "code": "+244",
+      "name": "Angola"
+    },
+    {
+      "code": "+1 264",
+      "name": "Anguilla"
+    },
+    {
+      "code": "+1 268",
+      "name": "Antigua and Barbuda"
+    },
+    {
+      "code": "+54",
+      "name": "Argentina"
+    },
+    {
+      "code": "+374",
+      "name": "Armenia"
+    },
+    {
+      "code": "+297",
+      "name": "Aruba"
+    },
+    {
+      "code": "+247",
+      "name": "Ascension"
+    },
+    {
+      "code": "+61",
+      "name": "Australia"
+    },
+    {
+      "code": "+672",
+      "name": "Australian External Territories"
+    },
+    {
+      "code": "+43",
+      "name": "Austria"
+    },
+    {
+      "code": "+994",
+      "name": "Azerbaijan"
+    },
+    {
+      "code": "+1 242",
+      "name": "Bahamas"
+    },
+    {
+      "code": "+973",
+      "name": "Bahrain"
+    },
+    {
+      "code": "+880",
+      "name": "Bangladesh"
+    },
+    {
+      "code": "+1 246",
+      "name": "Barbados"
+    },
+    {
+      "code": "+1 268",
+      "name": "Barbuda"
+    },
+    {
+      "code": "+375",
+      "name": "Belarus"
+    },
+    {
+      "code": "+32",
+      "name": "Belgium"
+    },
+    {
+      "code": "+501",
+      "name": "Belize"
+    },
+    {
+      "code": "+229",
+      "name": "Benin"
+    },
+    {
+      "code": "+1 441",
+      "name": "Bermuda"
+    },
+    {
+      "code": "+975",
+      "name": "Bhutan"
+    },
+    {
+      "code": "+591",
+      "name": "Bolivia"
+    },
+    {
+      "code": "+387",
+      "name": "Bosnia and Herzegovina"
+    },
+    {
+      "code": "+267",
+      "name": "Botswana"
+    },
+    {
+      "code": "+55",
+      "name": "Brazil"
+    },
+    {
+      "code": "+246",
+      "name": "British Indian Ocean Territory"
+    },
+    {
+      "code": "+1 284",
+      "name": "British Virgin Islands"
+    },
+    {
+      "code": "+673",
+      "name": "Brunei"
+    },
+    {
+      "code": "+359",
+      "name": "Bulgaria"
+    },
+    {
+      "code": "+226",
+      "name": "Burkina Faso"
+    },
+    {
+      "code": "+257",
+      "name": "Burundi"
+    },
+    {
+      "code": "+855",
+      "name": "Cambodia"
+    },
+    {
+      "code": "+237",
+      "name": "Cameroon"
+    },
+    {
+      "code": "+1",
+      "name": "Canada"
+    },
+    {
+      "code": "+238",
+      "name": "Cape Verde"
+    },
+    {
+      "code": "+ 345",
+      "name": "Cayman Islands"
+    },
+    {
+      "code": "+236",
+      "name": "Central African Republic"
+    },
+    {
+      "code": "+235",
+      "name": "Chad"
+    },
+    {
+      "code": "+56",
+      "name": "Chile"
+    },
+    {
+      "code": "+86",
+      "name": "China"
+    },
+    {
+      "code": "+61",
+      "name": "Christmas Island"
+    },
+    {
+      "code": "+61",
+      "name": "Cocos-Keeling Islands"
+    },
+    {
+      "code": "+57",
+      "name": "Colombia"
+    },
+    {
+      "code": "+269",
+      "name": "Comoros"
+    },
+    {
+      "code": "+242",
+      "name": "Congo"
+    },
+    {
+      "code": "+243",
+      "name": "Congo, Dem. Rep. of (Zaire)"
+    },
+    {
+      "code": "+682",
+      "name": "Cook Islands"
+    },
+    {
+      "code": "+506",
+      "name": "Costa Rica"
+    },
+    {
+      "code": "+385",
+      "name": "Croatia"
+    },
+    {
+      "code": "+53",
+      "name": "Cuba"
+    },
+    {
+      "code": "+599",
+      "name": "Curacao"
+    },
+    {
+      "code": "+537",
+      "name": "Cyprus"
+    },
+    {
+      "code": "+420",
+      "name": "Czech Republic"
+    },
+    {
+      "code": "+45",
+      "name": "Denmark"
+    },
+    {
+      "code": "+246",
+      "name": "Diego Garcia"
+    },
+    {
+      "code": "+253",
+      "name": "Djibouti"
+    },
+    {
+      "code": "+1 767",
+      "name": "Dominica"
+    },
+    {
+      "code": "+1 809",
+      "name": "Dominican Republic"
+    },
+    {
+      "code": "+670",
+      "name": "East Timor"
+    },
+    {
+      "code": "+56",
+      "name": "Easter Island"
+    },
+    {
+      "code": "+593",
+      "name": "Ecuador"
+    },
+    {
+      "code": "+20",
+      "name": "Egypt"
+    },
+    {
+      "code": "+503",
+      "name": "El Salvador"
+    },
+    {
+      "code": "+240",
+      "name": "Equatorial Guinea"
+    },
+    {
+      "code": "+291",
+      "name": "Eritrea"
+    },
+    {
+      "code": "+372",
+      "name": "Estonia"
+    },
+    {
+      "code": "+251",
+      "name": "Ethiopia"
+    },
+    {
+      "code": "+500",
+      "name": "Falkland Islands"
+    },
+    {
+      "code": "+298",
+      "name": "Faroe Islands"
+    },
+    {
+      "code": "+679",
+      "name": "Fiji"
+    },
+    {
+      "code": "+358",
+      "name": "Finland"
+    },
+    {
+      "code": "+33",
+      "name": "France"
+    },
+    {
+      "code": "+596",
+      "name": "French Antilles"
+    },
+    {
+      "code": "+594",
+      "name": "French Guiana"
+    },
+    {
+      "code": "+689",
+      "name": "French Polynesia"
+    },
+    {
+      "code": "+241",
+      "name": "Gabon"
+    },
+    {
+      "code": "+220",
+      "name": "Gambia"
+    },
+    {
+      "code": "+995",
+      "name": "Georgia"
+    },
+    {
+      "code": "+49",
+      "name": "Germany"
+    },
+    {
+      "code": "+233",
+      "name": "Ghana"
+    },
+    {
+      "code": "+350",
+      "name": "Gibraltar"
+    },
+    {
+      "code": "+30",
+      "name": "Greece"
+    },
+    {
+      "code": "+299",
+      "name": "Greenland"
+    },
+    {
+      "code": "+1 473",
+      "name": "Grenada"
+    },
+    {
+      "code": "+590",
+      "name": "Guadeloupe"
+    },
+    {
+      "code": "+1 671",
+      "name": "Guam"
+    },
+    {
+      "code": "+502",
+      "name": "Guatemala"
+    },
+    {
+      "code": "+224",
+      "name": "Guinea"
+    },
+    {
+      "code": "+245",
+      "name": "Guinea-Bissau"
+    },
+    {
+      "code": "+595",
+      "name": "Guyana"
+    },
+    {
+      "code": "+509",
+      "name": "Haiti"
+    },
+    {
+      "code": "+504",
+      "name": "Honduras"
+    },
+    {
+      "code": "+852",
+      "name": "Hong Kong SAR China"
+    },
+    {
+      "code": "+36",
+      "name": "Hungary"
+    },
+    {
+      "code": "+354",
+      "name": "Iceland"
+    },
+    {
+      "code": "+91",
+      "name": "India"
+    },
+    {
+      "code": "+62",
+      "name": "Indonesia"
+    },
+    {
+      "code": "+98",
+      "name": "Iran"
+    },
+    {
+      "code": "+964",
+      "name": "Iraq"
+    },
+    {
+      "code": "+353",
+      "name": "Ireland"
+    },
+    {
+      "code": "+972",
+      "name": "Israel"
+    },
+    {
+      "code": "+39",
+      "name": "Italy"
+    },
+    {
+      "code": "+225",
+      "name": "Ivory Coast"
+    },
+    {
+      "code": "+1 876",
+      "name": "Jamaica"
+    },
+    {
+      "code": "+81",
+      "name": "Japan"
+    },
+    {
+      "code": "+962",
+      "name": "Jordan"
+    },
+    {
+      "code": "+7 7",
+      "name": "Kazakhstan"
+    },
+    {
+      "code": "+254",
+      "name": "Kenya"
+    },
+    {
+      "code": "+686",
+      "name": "Kiribati"
+    },
+    {
+      "code": "+965",
+      "name": "Kuwait"
+    },
+    {
+      "code": "+996",
+      "name": "Kyrgyzstan"
+    },
+    {
+      "code": "+856",
+      "name": "Laos"
+    },
+    {
+      "code": "+371",
+      "name": "Latvia"
+    },
+    {
+      "code": "+961",
+      "name": "Lebanon"
+    },
+    {
+      "code": "+266",
+      "name": "Lesotho"
+    },
+    {
+      "code": "+231",
+      "name": "Liberia"
+    },
+    {
+      "code": "+218",
+      "name": "Libya"
+    },
+    {
+      "code": "+423",
+      "name": "Liechtenstein"
+    },
+    {
+      "code": "+370",
+      "name": "Lithuania"
+    },
+    {
+      "code": "+352",
+      "name": "Luxembourg"
+    },
+    {
+      "code": "+853",
+      "name": "Macau SAR China"
+    },
+    {
+      "code": "+389",
+      "name": "Macedonia"
+    },
+    {
+      "code": "+261",
+      "name": "Madagascar"
+    },
+    {
+      "code": "+265",
+      "name": "Malawi"
+    },
+    {
+      "code": "+60",
+      "name": "Malaysia"
+    },
+    {
+      "code": "+960",
+      "name": "Maldives"
+    },
+    {
+      "code": "+223",
+      "name": "Mali"
+    },
+    {
+      "code": "+356",
+      "name": "Malta"
+    },
+    {
+      "code": "+692",
+      "name": "Marshall Islands"
+    },
+    {
+      "code": "+596",
+      "name": "Martinique"
+    },
+    {
+      "code": "+222",
+      "name": "Mauritania"
+    },
+    {
+      "code": "+230",
+      "name": "Mauritius"
+    },
+    {
+      "code": "+262",
+      "name": "Mayotte"
+    },
+    {
+      "code": "+52",
+      "name": "Mexico"
+    },
+    {
+      "code": "+691",
+      "name": "Micronesia"
+    },
+    {
+      "code": "+1 808",
+      "name": "Midway Island"
+    },
+    {
+      "code": "+373",
+      "name": "Moldova"
+    },
+    {
+      "code": "+377",
+      "name": "Monaco"
+    },
+    {
+      "code": "+976",
+      "name": "Mongolia"
+    },
+    {
+      "code": "+382",
+      "name": "Montenegro"
+    },
+    {
+      "code": "+1664",
+      "name": "Montserrat"
+    },
+    {
+      "code": "+212",
+      "name": "Morocco"
+    },
+    {
+      "code": "+95",
+      "name": "Myanmar"
+    },
+    {
+      "code": "+264",
+      "name": "Namibia"
+    },
+    {
+      "code": "+674",
+      "name": "Nauru"
+    },
+    {
+      "code": "+977",
+      "name": "Nepal"
+    },
+    {
+      "code": "+31",
+      "name": "Netherlands"
+    },
+    {
+      "code": "+599",
+      "name": "Netherlands Antilles"
+    },
+    {
+      "code": "+1 869",
+      "name": "Nevis"
+    },
+    {
+      "code": "+687",
+      "name": "New Caledonia"
+    },
+    {
+      "code": "+64",
+      "name": "New Zealand"
+    },
+    {
+      "code": "+505",
+      "name": "Nicaragua"
+    },
+    {
+      "code": "+227",
+      "name": "Niger"
+    },
+    {
+      "code": "+234",
+      "name": "Nigeria"
+    },
+    {
+      "code": "+683",
+      "name": "Niue"
+    },
+    {
+      "code": "+672",
+      "name": "Norfolk Island"
+    },
+    {
+      "code": "+850",
+      "name": "North Korea"
+    },
+    {
+      "code": "+1 670",
+      "name": "Northern Mariana Islands"
+    },
+    {
+      "code": "+47",
+      "name": "Norway"
+    },
+    {
+      "code": "+968",
+      "name": "Oman"
+    },
+    {
+      "code": "+92",
+      "name": "Pakistan"
+    },
+    {
+      "code": "+680",
+      "name": "Palau"
+    },
+    {
+      "code": "+970",
+      "name": "Palestinian Territory"
+    },
+    {
+      "code": "+507",
+      "name": "Panama"
+    },
+    {
+      "code": "+675",
+      "name": "Papua New Guinea"
+    },
+    {
+      "code": "+595",
+      "name": "Paraguay"
+    },
+    {
+      "code": "+51",
+      "name": "Peru"
+    },
+    {
+      "code": "+63",
+      "name": "Philippines"
+    },
+    {
+      "code": "+48",
+      "name": "Poland"
+    },
+    {
+      "code": "+351",
+      "name": "Portugal"
+    },
+    {
+      "code": "+1 787",
+      "name": "Puerto Rico"
+    },
+    {
+      "code": "+974",
+      "name": "Qatar"
+    },
+    {
+      "code": "+262",
+      "name": "Reunion"
+    },
+    {
+      "code": "+40",
+      "name": "Romania"
+    },
+    {
+      "code": "+7",
+      "name": "Russia"
+    },
+    {
+      "code": "+250",
+      "name": "Rwanda"
+    },
+    {
+      "code": "+685",
+      "name": "Samoa"
+    },
+    {
+      "code": "+378",
+      "name": "San Marino"
+    },
+    {
+      "code": "+966",
+      "name": "Saudi Arabia"
+    },
+    {
+      "code": "+221",
+      "name": "Senegal"
+    },
+    {
+      "code": "+381",
+      "name": "Serbia"
+    },
+    {
+      "code": "+248",
+      "name": "Seychelles"
+    },
+    {
+      "code": "+232",
+      "name": "Sierra Leone"
+    },
+    {
+      "code": "+65",
+      "name": "Singapore"
+    },
+    {
+      "code": "+421",
+      "name": "Slovakia"
+    },
+    {
+      "code": "+386",
+      "name": "Slovenia"
+    },
+    {
+      "code": "+677",
+      "name": "Solomon Islands"
+    },
+    {
+      "code": "+27",
+      "name": "South Africa"
+    },
+    {
+      "code": "+500",
+      "name": "South Georgia and the South Sandwich Islands"
+    },
+    {
+      "code": "+82",
+      "name": "South Korea"
+    },
+    {
+      "code": "+34",
+      "name": "Spain"
+    },
+    {
+      "code": "+94",
+      "name": "Sri Lanka"
+    },
+    {
+      "code": "+249",
+      "name": "Sudan"
+    },
+    {
+      "code": "+597",
+      "name": "Suriname"
+    },
+    {
+      "code": "+268",
+      "name": "Swaziland"
+    },
+    {
+      "code": "+46",
+      "name": "Sweden"
+    },
+    {
+      "code": "+41",
+      "name": "Switzerland"
+    },
+    {
+      "code": "+963",
+      "name": "Syria"
+    },
+    {
+      "code": "+886",
+      "name": "Taiwan"
+    },
+    {
+      "code": "+992",
+      "name": "Tajikistan"
+    },
+    {
+      "code": "+255",
+      "name": "Tanzania"
+    },
+    {
+      "code": "+66",
+      "name": "Thailand"
+    },
+    {
+      "code": "+670",
+      "name": "Timor Leste"
+    },
+    {
+      "code": "+228",
+      "name": "Togo"
+    },
+    {
+      "code": "+690",
+      "name": "Tokelau"
+    },
+    {
+      "code": "+676",
+      "name": "Tonga"
+    },
+    {
+      "code": "+1 868",
+      "name": "Trinidad and Tobago"
+    },
+    {
+      "code": "+216",
+      "name": "Tunisia"
+    },
+    {
+      "code": "+90",
+      "name": "Turkey"
+    },
+    {
+      "code": "+993",
+      "name": "Turkmenistan"
+    },
+    {
+      "code": "+1 649",
+      "name": "Turks and Caicos Islands"
+    },
+    {
+      "code": "+688",
+      "name": "Tuvalu"
+    },
+    {
+      "code": "+1 340",
+      "name": "U.S. Virgin Islands"
+    },
+    {
+      "code": "+256",
+      "name": "Uganda"
+    },
+    {
+      "code": "+380",
+      "name": "Ukraine"
+    },
+    {
+      "code": "+971",
+      "name": "United Arab Emirates"
+    },
+    {
+      "code": "+44",
+      "name": "United Kingdom"
+    },
+    {
+      "code": "+1",
+      "name": "United States"
+    },
+    {
+      "code": "+598",
+      "name": "Uruguay"
+    },
+    {
+      "code": "+998",
+      "name": "Uzbekistan"
+    },
+    {
+      "code": "+678",
+      "name": "Vanuatu"
+    },
+    {
+      "code": "+58",
+      "name": "Venezuela"
+    },
+    {
+      "code": "+84",
+      "name": "Vietnam"
+    },
+    {
+      "code": "+1 808",
+      "name": "Wake Island"
+    },
+    {
+      "code": "+681",
+      "name": "Wallis and Futuna"
+    },
+    {
+      "code": "+967",
+      "name": "Yemen"
+    },
+    {
+      "code": "+260",
+      "name": "Zambia"
+    },
+    {
+      "code": "+255",
+      "name": "Zanzibar"
+    },
+    {
+      "code": "+263",
+      "name": "Zimbabwe"
+    }
+]

+ 166 - 0
src/components/AuthPage/Authorization/index.tsx

@@ -0,0 +1,166 @@
+import qrCode from '../../../img/icons8-telegram-app.svg'
+import { makeStyles, Button, TextField, MenuItem } from '@material-ui/core'
+import Checkbox from '@mui/material/Checkbox';
+import FormControlLabel from '@mui/material/FormControlLabel';
+import React from 'react';
+
+import { countries } from './countries'
+
+
+
+const useStyles = makeStyles({
+  container: {
+    display: 'flex',
+    alignItems: 'center',
+    alignContent:'center',
+    flexDirection: 'column',
+    width: 350,
+    margin: '0 auto',
+    paddingTop: 64,
+    paddingBottom: 24,
+  },
+  picture: {
+    marginBottom: 16,
+    paddingLeft: 45
+  },
+  title: {
+    textAlign: 'center',
+    marginBottom: 16,
+  },
+  titleConfirm: {
+    color: '#b4b4b4',
+    fontWeight:400
+  },
+  buttonQR: {
+    marginTop: 20,
+    height: 50,
+    color: '#5497f0'
+  },
+  buttonNext: {
+    marginTop: 20,
+    height: 50,
+    color: '#f8f8f8',
+    backgroundColor:'#1d74c5',
+  },
+  textField: {
+    marginBottom:20
+  },
+})
+
+interface IAuthorizationForm {
+  handleIsQR: () => void,
+  handleIsCheck:(e:React.ChangeEvent<HTMLInputElement>) => void
+  handleSendCode: () => Promise<void>,
+  number:string,
+  setNumber: React.Dispatch<React.SetStateAction<string>>,
+  country: string,
+  setCountry: React.Dispatch<React.SetStateAction<string>>,
+  isChecked: boolean,
+}
+interface ICountry {
+  name: string,
+  code: string
+}
+
+const Authorization = ({
+  handleIsQR,
+  handleIsCheck,
+  handleSendCode,
+  number,
+  setNumber,
+  country,
+  setCountry,
+  isChecked,
+}: IAuthorizationForm) => {
+  const classes = useStyles()
+
+    const isValidCredentials = () => {
+    const numberLength = number.split(' ').join('').trim().length
+    if (numberLength < 13 || numberLength > 13) return false
+    if (!country || country === 'Country') return false
+    if(number.slice(0,1) === '+') return true
+    return true
+  }
+
+
+  const handleTextField = (e: React.ChangeEvent<HTMLInputElement>) => {
+    const value = e.target.value
+    const name = e.target.name
+    switch (name) {
+      case 'country':
+        setCountry(value)
+        const code = countries.find(el => el.name === value)?.code
+        code&&setNumber(code)
+        break;
+      case 'number':
+        setNumber(value)
+        break;
+      default:
+        break;
+    }
+  }
+
+  return (
+    <div className={classes.container} >
+      <img className={classes.picture} alt="QRCode" src={qrCode} width='270' height='270' />
+      <h2 className={classes.title}>Telegram</h2>
+      <h4 className={classes.title + ' ' + classes.titleConfirm}>
+        Please confirm your country code and enter your phone number.
+      </h4>
+      <TextField
+        id="country"
+        name='country'
+        label="Country"
+        fullWidth
+        variant='outlined'
+        defaultValue=''
+        select
+        className={classes.textField}
+        onChange={handleTextField}
+      >
+        {countries.map(({name}: ICountry) => (
+          <MenuItem value={name}  key={name}>
+            {name}
+          </MenuItem>
+        ))}
+      </TextField>
+      <TextField
+        id="number"
+        name='number'
+        label="Your phone number"
+        value={number}
+        fullWidth
+        variant='outlined'
+        onChange={handleTextField}
+      />
+      <FormControlLabel
+        label="Keep me signed in"
+        control={<Checkbox
+         checked={isChecked}
+         onChange={handleIsCheck}
+         inputProps={{ 'aria-label': 'controlled' }}
+        />}
+      />
+      {isValidCredentials() &&
+        <Button
+        onClick={handleSendCode}
+        className={classes.buttonNext}
+        color='primary'
+        variant="contained"
+        fullWidth>
+        NEXT
+      </Button>}
+
+      <Button
+        onClick={handleIsQR}
+        className={classes.buttonQR}
+        variant="text"
+        color="default"
+        fullWidth>
+        LOG IN BY QR CODE
+      </Button>
+    </div>
+  );
+};
+
+export default Authorization;

+ 76 - 0
src/components/AuthPage/QRCode/index.tsx

@@ -0,0 +1,76 @@
+import qrCode from '../../../img/qrCode_telegram.png'
+import { makeStyles, Button, } from '@material-ui/core'
+
+const useStyles = makeStyles({
+  container: {
+    display: 'flex',
+    alignItems: 'center',
+    alignContent:'center',
+    flexDirection: 'column',
+    width: 350,
+    margin: '0 auto',
+    paddingTop: 64,
+    paddingBottom: 24,
+  },
+  picture: {
+    marginBottom: 16,
+  },
+  title: {
+    textAlign: 'center',
+    marginBottom: 16,
+  },
+  titleSpan: {
+    display: 'flex',
+    justifyContent: 'center',
+    alignContent: 'center',
+    alignItems: 'center',
+    width: 25,
+    height: 25,
+    position: 'absolute',
+    left:780,
+    backgroundColor: '#5497f0',
+    borderRadius: '50%',
+    color: '#ffffff',
+  },
+  button: {
+    marginTop: 20,
+    height: 50,
+    color: '#5497f0'
+  }
+})
+
+interface IQRCode {
+  handleIsQR:() => void
+}
+
+const QRCode = ({ handleIsQR }: IQRCode) => {
+
+  const classes = useStyles()
+  return (
+    <div className={classes.container} >
+      <img className={classes.picture} alt="QRCode" src={qrCode} width='300' height='300' />
+      <h2 className={classes.title}>Log in to Telegram by QR Code</h2>
+      <h3 className={classes.title}>
+         <span className={classes.titleSpan}>1</span>
+        Open Telegram on your phone
+      </h3>
+      <h3 className={classes.title}>
+        <span className={classes.titleSpan}>2</span>
+        {`Go to Settings > Devices > Link Desktop Device`}
+      </h3>
+      <h3 className={classes.title}>
+        <span className={classes.titleSpan}>3</span>
+        Log in to Telegram by QR Code
+      </h3>
+      <Button onClick={handleIsQR}
+        className={classes.button}
+        variant="text"
+        color="default"
+        fullWidth>
+        LOG IN BY PHONE NUMBER
+      </Button>
+    </div>
+  );
+};
+
+export default QRCode;

+ 111 - 0
src/components/AuthPage/Registration/index.tsx

@@ -0,0 +1,111 @@
+import { makeStyles, Button, TextField, Typography } from '@material-ui/core'
+import {useState} from 'react';
+
+const useStyles = makeStyles({
+  container: {
+    display: 'flex',
+    alignItems: 'center',
+    alignContent:'center',
+    flexDirection: 'column',
+    width: 350,
+    margin: '0 auto',
+    paddingTop: 64,
+    paddingBottom: 24,
+  },
+  title: {
+    marginBottom:20
+  },
+  buttonNext: {
+    marginTop: 20,
+    height: 50,
+    color: '#f8f8f8',
+    backgroundColor:'#1d74c5',
+  },
+  textField: {
+    marginBottom:20
+  }
+})
+
+interface IRegistration {
+}
+
+
+const Registration = () => {
+  const classes = useStyles()
+  const [name, setName] = useState<string>('')
+  const [lastName, setLastName] = useState<string>('')
+  
+  const format = (a: string) => a.split(' ').join('').trim()
+
+  const isValidCredentials = () => {
+    
+    const validName = name.length
+    const validLastName = lastName.length
+    if (validName < 3 || validName > 30) return false
+    if (validLastName < 3 || validLastName > 30) return false
+    return true
+  }
+
+
+  const handleTextField = (e: React.ChangeEvent<HTMLInputElement>) => {
+    const value = format(e.target.value)
+    const name = e.target.name
+    switch (name) {
+      case 'name':
+        setName(value)
+        break;
+      case 'lastName':
+        setLastName(value)
+        break;
+      default:
+        break;
+    }
+  }
+
+  const handleUpdateUser = () => {
+      console.log(name,lastName)
+  }
+
+  return (
+    <div className={classes.container} >
+      <Typography
+        className={classes.title}
+        variant="h5"
+        color="initial">
+        Fill registration form
+      </Typography>
+      <TextField
+        id="name"
+        name='name'
+        label="Name"
+        value={name}
+        fullWidth
+        variant='outlined'
+        onChange={handleTextField}
+        className={classes.textField}
+        />
+      <TextField
+        id="lastName"
+        name='lastName'
+        label="LastName"
+        value={lastName}
+        fullWidth
+        variant='outlined'
+        onChange={handleTextField}
+        className={classes.textField}
+        />
+
+      {isValidCredentials() &&
+        <Button
+        onClick={handleUpdateUser}
+        className={classes.buttonNext}
+        color='primary'
+        variant="contained"
+        fullWidth
+        > NEXT
+      </Button>}
+    </div>
+  );
+};
+
+export default Registration;

+ 79 - 0
src/components/AuthPage/SMSCode/index.tsx

@@ -0,0 +1,79 @@
+import monkeyPng from '../../../img/monkey.png'
+import { makeStyles, TextField, Typography, IconButton } from '@material-ui/core'
+import ModeEditOutlineOutlinedIcon from '@mui/icons-material/ModeEditOutlineOutlined';
+import React from 'react';
+
+
+
+
+
+const useStyles = makeStyles({
+  container: {
+    display: 'flex',
+    alignItems: 'center',
+    alignContent:'center',
+    flexDirection: 'column',
+    width: 350,
+    margin: '0 auto',
+    paddingTop: 64,
+    paddingBottom: 24,
+  },
+  picture: {
+    marginBottom: 70,
+    },
+  titleConfirm: {
+    color: '#b4b4b4',
+    fontWeight: 400,
+    textAlign: 'center',
+    marginBottom: 20,
+  },
+  buttonSubmit: {
+    marginTop: 20,
+    height: 50,
+    color: '#5497f0'
+  },
+  textField: {
+    marginBottom:20
+  },
+})
+
+
+interface ISMSCode {
+  handleIsValidCode: (e: React.ChangeEvent<HTMLInputElement>) => void,
+  number: string,
+  handleResetState: () => void,
+}
+const SMSCode = ({handleIsValidCode,number,handleResetState}:ISMSCode) => {
+  const classes = useStyles()
+
+  return (
+    <div className={classes.container} >
+      <img className={classes.picture} alt="QRCode" src={monkeyPng} width='150' height='150' />
+      <Typography variant="h4" color="initial">
+        {number}
+        <IconButton
+          size="medium"
+          aria-label='secondary'
+          onClick={handleResetState}
+           >
+          <ModeEditOutlineOutlinedIcon />
+        </IconButton> 
+      </Typography>
+      <h3 className={classes.titleConfirm}>
+        We've sent the code to the Telegram app on your other device.
+      </h3>
+      <TextField
+        id="code"
+        name='code'
+        label="Code"
+        variant='outlined'
+        fullWidth
+        className={classes.textField}
+        onChange={handleIsValidCode}
+      />
+    
+    </div>
+  );
+};
+
+export default SMSCode;

+ 72 - 0
src/components/AuthPage/index.tsx

@@ -0,0 +1,72 @@
+import { useState, useEffect } from 'react';
+import { useDispatch } from 'react-redux';
+
+import QRCode from './QRCode'
+import Authorization from './Authorization'
+import SMSCode from './SMSCode'
+import Registration from './Registration'
+import { authorizeUser } from '../../api-data'
+import {asyncLogin} from '../../redux/authorization/operations'
+
+
+
+
+
+const AuthPage = () => {
+  const [isQR, setIsQR] = useState<boolean>(false)
+  const [isCode, setIsCode] = useState<string>('12221')
+  const [isChecked, setIsChecked] = useState<boolean>(true);
+  const [isReg, setIsReg] = useState<boolean>(false);
+  const [country, setCountry] = useState<string>('Country')
+  const [number, setNumber] = useState<string>('+380995688412')
+
+  const dispatch = useDispatch()
+
+  useEffect(() => {
+    const isCheckedLC = localStorage.isChecked
+    isCheckedLC && setIsChecked(isCheckedLC === 'true'?true:false)
+  }, [])
+
+  const handleIsQR = (): void => setIsQR(!isQR)
+
+  const handleSendCode = async (): Promise<void> => {
+      const res = await authorizeUser(number.split(' ').join('').trim(), country)
+      res&&setIsCode(res)
+  }
+
+  const handleIsValidCode = async (e: React.ChangeEvent<HTMLInputElement>):Promise<void> => {
+    const value = e.target.value.trim().split(' ').join('');
+    if (isCode && value === isCode)
+      dispatch(await asyncLogin(number, isCode, () => setIsReg(!isReg)))
+    }
+  const handleIsCheck = (e: React.ChangeEvent<HTMLInputElement>): void => {
+    const flag = e.target.checked
+    setIsChecked(flag);
+    localStorage.isChecked = flag
+  }
+
+  const handleResetState = ():void => {
+    setIsCode('')
+    setCountry('Country')
+    setNumber('')
+  }
+ 
+  return (
+    isReg?<Registration/>:
+    isCode ? <SMSCode
+      handleIsValidCode={handleIsValidCode}
+      number={number}
+      handleResetState={handleResetState}/> :
+      isQR ? <QRCode handleIsQR={handleIsQR} /> :
+        <Authorization handleIsQR={handleIsQR}
+          handleSendCode={handleSendCode}
+          handleIsCheck={handleIsCheck}
+          setNumber={setNumber}
+          number={number}
+          country={country}
+          setCountry={setCountry}
+          isChecked={isChecked} />
+  )
+};
+
+export default AuthPage;

+ 18 - 0
src/components/Loader/Loader.jsx

@@ -0,0 +1,18 @@
+import 'react-loader-spinner/dist/loader/css/react-spinner-loader.css';
+import Loader from 'react-loader-spinner';
+
+import s from './Loader.module.css';
+
+const Load = () => {
+  return (
+    <Loader
+      className={s.loader}
+      type="Puff"
+      color="#0ca0f5"
+      height={100}
+      width={100}
+      timeout={300000}
+    />
+  );
+};
+export default Load;

+ 11 - 0
src/components/Loader/Loader.module.css

@@ -0,0 +1,11 @@
+.loader {
+  position: fixed;
+  display: inline-block;
+  width: 5vw;
+  height: 5vh;
+  top: 86vh;
+  left: 0;
+  right: 0;
+  margin-left: auto;
+  margin-right: 3vw;
+}

+ 18 - 0
src/components/Routes/PrivateRoute/PrivateRoute.tsx

@@ -0,0 +1,18 @@
+import { Route, Redirect } from 'react-router-dom';
+import { useSelector } from 'react-redux';
+
+import { IPrivateProps} from '../../../typescript/components/Routes/interfaces';
+import { getToken } from '../../../redux/authorization/selector'
+
+function PrivateRoute({ children, ...routeProps }: IPrivateProps) {
+  const token = useSelector(getToken)
+  return (
+    <Route {...routeProps}>
+      {token ? children : <Redirect to="/z/" />}
+    </Route>
+  );
+}
+
+
+
+export default PrivateRoute

+ 23 - 0
src/components/Routes/PublicRoute/PublicRoute.tsx

@@ -0,0 +1,23 @@
+import { Route, Redirect } from 'react-router-dom';
+import { useSelector } from 'react-redux';
+
+import { IPublicProps } from '../../../typescript/components/Routes/interfaces';
+import { getToken } from '../../../redux/authorization/selector'
+
+function PublicRoute({
+  children,
+  restricted = false,
+  ...routeProps
+}: IPublicProps) {
+  const token = useSelector(getToken)
+  const shouldRedirect = token && restricted;
+
+  return (
+    <Route {...routeProps}>
+      {shouldRedirect ? <Redirect to="/" /> : children}
+    </Route>
+  );
+}
+
+
+export default PublicRoute;

File diff suppressed because it is too large
+ 1 - 0
src/img/icons8-telegram-app.svg


BIN
src/img/monkey.png


BIN
src/img/qrCode_telegram.png


BIN
src/img/telegram.png


+ 54 - 0
src/index.css

@@ -0,0 +1,54 @@
+html {
+  overflow-y: scroll;
+  overflow-x: hidden;
+}
+
+body {
+  min-width: 100vw;
+  min-height: 100vh;
+  margin: 0;
+  padding: 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;
+}
+
+img {
+  display: block;
+  max-width: 100%;
+  height: auto;
+}
+
+.box.big {
+  width: 200px;
+  height: 200px;
+  margin-bottom: 100px;
+}
+
+.box.red {
+  background-color: teal;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5 {
+  margin: 0;
+  padding: 0;
+}
+ul {
+  padding: 0;
+  margin: 0;
+}
+
+
+
+@import 'https://fonts.googleapis.com/css?family=Mountains+of+Christmas';

+ 22 - 0
src/index.tsx

@@ -0,0 +1,22 @@
+import React from 'react';
+import { Provider } from 'react-redux';
+import { PersistGate } from 'redux-persist/integration/react';
+import ReactDOM from 'react-dom';
+import 'react-toastify/dist/ReactToastify.css';
+
+import 'modern-normalize/modern-normalize.css';
+import './index.css';
+import App from './App';
+import Loader from './components/Loader/Loader';
+import { store, persistor } from './redux/store';
+
+ReactDOM.render(
+  <React.StrictMode>
+    <PersistGate loading={<Loader/>} persistor={persistor}>
+      <Provider store={store}>
+        <App />
+      </Provider>
+    </PersistGate>
+  </React.StrictMode>,
+  document.getElementById('root'),
+);

+ 1 - 0
src/react-app-env.d.ts

@@ -0,0 +1 @@
+/// <reference types="react-scripts" />

+ 24 - 0
src/redux/authorization/action/index.ts

@@ -0,0 +1,24 @@
+import { createAction } from '@reduxjs/toolkit';
+
+const actionLogInSuccess = createAction('login/success', (value:string) => ({
+  payload: value,
+}));
+
+const actionLogInReject = createAction('login/reject', () => ({
+  payload: null,
+}));
+
+const actionLogOutSuccess = createAction('logout/success', () => ({
+  payload: null,
+}));
+
+const actionLogOutReject = createAction('logout/reject', () => ({
+  payload: null,
+}));
+
+export {
+  actionLogInSuccess,
+  actionLogInReject,
+  actionLogOutSuccess,
+  actionLogOutReject,
+};

+ 42 - 0
src/redux/authorization/operations/index.ts

@@ -0,0 +1,42 @@
+import { actionIsLoading } from '../../loading/action';
+
+import {
+  actionLogInSuccess,
+  actionLogInReject,
+  actionLogOutSuccess,
+  actionLogOutReject,
+} from '../action';
+
+import { setToken, loginUser } from '../../../api-data';
+
+
+const asyncLogin = (number:string, code: string,cb:() => void ) => async (dispatch:any) => {
+  try {
+    dispatch(actionIsLoading(true));
+    const data = await loginUser<{token:string}>(number, code);
+    if(!data?.token) throw new Error('bad request')
+      dispatch(actionLogInSuccess(data.token))
+      setToken.set(data.token)
+  } catch (e) {
+    dispatch(actionLogInReject());
+    cb()
+  } finally {
+    dispatch(actionIsLoading(false));
+  }
+};
+
+const asyncLogout = () => async (dispatch:any) => {
+  try {
+    dispatch(actionIsLoading(true));
+    dispatch(actionLogOutSuccess());
+    setToken.unset()
+  } catch (e) {
+    dispatch(actionLogOutReject());
+  } finally {
+    dispatch(actionIsLoading(false));
+  }
+};
+
+
+
+export { asyncLogin, asyncLogout };

+ 36 - 0
src/redux/authorization/reducer/index.ts

@@ -0,0 +1,36 @@
+import { createReducer } from '@reduxjs/toolkit';
+import { IAuthorizationState } from '../../../typescript/redux/authorization/interfaces';
+
+import {
+  actionLogInSuccess,
+  actionLogInReject,
+  actionLogOutSuccess,
+  actionLogOutReject,
+} from '../action';
+
+const initialState:IAuthorizationState = {
+  token: '',
+  number: '',
+  name: '',
+  lastName: '',
+  country:'',
+  avatarUrl: '',
+};
+
+
+const reducerAuthorization = createReducer(initialState, {
+  [actionLogInSuccess.type]: (state, { payload:token }: {payload: string}) => {
+    return {...state,token};
+  },
+  [actionLogInReject.type]: (state, _) => {
+    return state;
+  },
+  [actionLogOutSuccess.type]: (_state, _) => {
+    return initialState;
+  },
+  [actionLogOutReject.type]: (state, _) => {
+    return state;
+  },
+});
+
+export default reducerAuthorization;

+ 11 - 0
src/redux/authorization/selector/index.ts

@@ -0,0 +1,11 @@
+import {IState} from '../../../typescript/redux/interfaces'
+
+const getToken = (state: IState) => state.authorization.token;
+const getNumber = (state:IState) => state.authorization.number;
+const getName = (state: IState) => state.authorization.name;
+const getLastName = (state: IState) => state.authorization.lastName;
+const getCountry = (state: IState) => state.authorization.country;
+const getAvatarUrl = (state: IState) => state.authorization.avatarUrl;
+const getState= (state:IState) => state.authorization;
+
+export { getToken,getNumber,getName,getLastName,getCountry,getAvatarUrl,getState };

+ 7 - 0
src/redux/loading/action/index.ts

@@ -0,0 +1,7 @@
+import { createAction } from '@reduxjs/toolkit';
+
+const actionIsLoading = createAction('fetch/loading', (value:boolean) => ({
+  payload: value,
+}));
+
+export { actionIsLoading };

+ 7 - 0
src/redux/loading/reducer/index.ts

@@ -0,0 +1,7 @@
+import { createReducer } from '@reduxjs/toolkit';
+import { actionIsLoading } from '../action';
+
+const reducerLoading = createReducer(false, {
+  [actionIsLoading.type]: (_, { payload }: {payload : boolean}) => payload,
+});
+export default reducerLoading;

+ 4 - 0
src/redux/loading/selector/index.ts

@@ -0,0 +1,4 @@
+import { IState } from '../../../typescript/redux/interfaces'
+const getLoad = (state:IState) => state.isLoading;
+
+export { getLoad };

+ 19 - 0
src/redux/rootReducer/index.ts

@@ -0,0 +1,19 @@
+import { combineReducers } from '@reduxjs/toolkit';
+import { persistReducer } from 'redux-persist';
+import storage from 'redux-persist/lib/storage';
+
+import reducerLoading from '../loading/reducer';
+import reducerAuthorization from '../authorization/reducer';
+
+const authorizationPersistConfig = {
+  key: 'auth',
+  storage: storage,
+};
+
+export const rootReducer = combineReducers({
+  isLoading: reducerLoading,
+  authorization: persistReducer(
+    authorizationPersistConfig,
+    reducerAuthorization,
+  ),
+});

+ 14 - 0
src/redux/store/index.ts

@@ -0,0 +1,14 @@
+import { createStore, applyMiddleware } from 'redux';
+import thunk from 'redux-thunk';
+import { persistStore } from 'redux-persist';
+import { composeWithDevTools } from 'redux-devtools-extension';
+
+import { rootReducer } from '../rootReducer';
+
+const composeEnhancers = composeWithDevTools({});
+const store = createStore(
+  rootReducer,
+  composeEnhancers(applyMiddleware(thunk)),
+);
+const persistor = persistStore(store);
+export { store, persistor };

+ 15 - 0
src/typescript/components/Routes/interfaces.ts

@@ -0,0 +1,15 @@
+export interface IPrivateProps {
+  children: React.ReactNode;
+  token?: string;
+  exact?: any;
+  path: string;
+}
+
+
+export interface IPublicProps {
+  children: React.ReactNode;
+  restricted?: boolean;
+  token?: string;
+  exact?: any;
+  path: string;
+}

+ 13 - 0
src/typescript/redux/authorization/interfaces.ts

@@ -0,0 +1,13 @@
+export interface IAuthorizationState  {
+        token: string,
+        number: string ,
+        name: string | null,
+        lastName: string | null,
+        country: string,
+        avatarUrl: string | null
+  }
+
+export interface IAuthorizationPayload  {
+  payload: IAuthorizationState
+}
+

+ 6 - 0
src/typescript/redux/interfaces.ts

@@ -0,0 +1,6 @@
+import {IAuthorizationState} from './authorization/interfaces'
+
+export interface IState {
+  isLoading: boolean;
+    authorization: IAuthorizationState
+}

+ 26 - 0
tsconfig.json

@@ -0,0 +1,26 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "lib": [
+      "dom",
+      "dom.iterable",
+      "esnext"
+    ],
+    "allowJs": true,
+    "skipLibCheck": true,
+    "esModuleInterop": true,
+    "allowSyntheticDefaultImports": true,
+    "strict": true,
+    "forceConsistentCasingInFileNames": true,
+    "noFallthroughCasesInSwitch": true,
+    "module": "esnext",
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "noEmit": true,
+    "jsx": "react-jsx"
+  },
+  "include": [
+    "src"
+  ]
+}