Przeglądaj źródła

done some fixes

unknown 3 lat temu
rodzic
commit
11fed0e24f
45 zmienionych plików z 946 dodań i 46 usunięć
  1. 1 1
      .eslintcache
  2. 59 9
      src/App.tsx
  3. 5 0
      src/components/Authorization/Authorization.module.css
  4. 13 0
      src/components/Authorization/Authorization.tsx
  5. 53 0
      src/components/Authorization/Registration/RegistrationForm.module.css
  6. 80 0
      src/components/Authorization/Registration/RegistrationForm.tsx
  7. 54 0
      src/components/Authorization/SignIn/SignIn.module.css
  8. 80 0
      src/components/Authorization/SignIn/SignIn.tsx
  9. 10 0
      src/components/Authorization/helpers/helpers.ts
  10. 27 0
      src/components/Authorization/helpers/validation.js
  11. 1 1
      src/components/Categories/Categories.tsx
  12. 2 6
      src/components/Categories/Category/Category.tsx
  13. 4 2
      src/components/Goods/DetailGood/DetailGood.tsx
  14. 1 1
      src/components/Goods/Goods.tsx
  15. 14 12
      src/components/Navigation/Navigation.tsx
  16. 15 0
      src/components/Orders/DetailOrder/DetailOrder.module.css
  17. 47 0
      src/components/Orders/DetailOrder/DetailOrder.tsx
  18. 15 0
      src/components/Orders/Order/Order.module.css
  19. 20 0
      src/components/Orders/Order/Order.tsx
  20. 16 0
      src/components/Orders/Orders.module.css
  21. 46 0
      src/components/Orders/Orders.tsx
  22. 21 0
      src/components/Routes/PrivateRoute/PrivateRoute.tsx
  23. 28 0
      src/components/Routes/PublicRoute/PublicRoute.tsx
  24. 0 12
      src/index.tsx
  25. 29 0
      src/redux/authorization/action/index.ts
  26. 6 0
      src/redux/authorization/actionType/index.ts
  27. 54 0
      src/redux/authorization/operation/index.ts
  28. 25 0
      src/redux/authorization/reducer/index.ts
  29. 6 1
      src/redux/categories/operation/index.ts
  30. 5 1
      src/redux/goods/operation/index.ts
  31. 14 0
      src/redux/orders/action/index.ts
  32. 4 0
      src/redux/orders/actionType/index.ts
  33. 22 0
      src/redux/orders/operation/index.ts
  34. 22 0
      src/redux/orders/reducer/index.ts
  35. 4 0
      src/redux/rootReducer/index.ts
  36. 4 0
      src/typescript/categories/interfaces.ts
  37. 3 0
      src/typescript/navigation/interfaces.ts
  38. 40 0
      src/typescript/orders/interfaces.ts
  39. 9 0
      src/typescript/orders/types.ts
  40. 6 0
      src/typescript/reduxTs/authorization/enum.ts
  41. 23 0
      src/typescript/reduxTs/authorization/interfaces.ts
  42. 4 0
      src/typescript/reduxTs/orders/enum.ts
  43. 7 0
      src/typescript/reduxTs/orders/interfaces.ts
  44. 21 0
      src/typescript/registration/interfaces.ts
  45. 26 0
      src/typescript/routes/interfaces.ts

Plik diff jest za duży
+ 1 - 1
.eslintcache


+ 59 - 9
src/App.tsx

@@ -6,6 +6,9 @@ import { ToastContainer } from 'react-toastify';
 import s from './App.module.css';
 import { IAppProps, IAppState } from './typescript/app/interfaces';
 
+import PrivateRoute from './components/Routes/PrivateRoute/PrivateRoute';
+import PublicRoute from './components/Routes/PublicRoute/PublicRoute';
+
 const Navigation = lazy(
   () =>
     import(
@@ -31,27 +34,74 @@ const DetailGood = lazy(
     ),
 );
 
+const Orders = lazy(
+  () => import('./components/Orders/Orders' /* webpackChunkName: "Orders" */),
+);
+
+const DetailOrder = lazy(
+  () =>
+    import(
+      './components/Orders/DetailOrder/DetailOrder' /* webpackChunkName: "DetailOrder" */
+    ),
+);
+
+const Authorization = lazy(
+  () =>
+    import(
+      './components/Authorization/Authorization' /* webpackChunkName: "AuthorizationPage" */
+    ),
+);
+
+const SignInForm = lazy(
+  () =>
+    import(
+      './components/Authorization/SignIn/SignIn' /* webpackChunkName: "SignInForm" */
+    ),
+);
+
+const RegistrationForm = lazy(
+  () =>
+    import(
+      './components/Authorization/Registration/RegistrationForm' /* webpackChunkName: "RegistrationForm" */
+    ),
+);
+
 const Loader = lazy(
   () => import('./components/Loader/Loader' /* webpackChunkName: "Loader" */),
 );
 
 function App({ isLoading }: IAppProps) {
-  console.log(isLoading, 'isLoading');
   return (
     <div className={s.appWrapper}>
       <BrowserRouter>
-        <Navigation />
         <Switch>
-          <Route exact path="/categories">
+          <PrivateRoute path="/">
+            <Navigation />
+          </PrivateRoute>
+          <PrivateRoute exact path="/categories">
             <Categories />
-          </Route>
-          <Route exact path="/categories/goods/:name">
+          </PrivateRoute>
+          <PrivateRoute exact path="/categories/goods/:name">
             <Goods />
-          </Route>
-          <Route exact path="/categories/goods/:name/:id">
+          </PrivateRoute>
+          <PrivateRoute exact path="/categories/goods/:name/:id">
             <DetailGood />
-          </Route>
-          <Route></Route>
+          </PrivateRoute>
+          <PrivateRoute exact path="/orders">
+            <Orders />
+          </PrivateRoute>
+          <PrivateRoute path="/orders/:id">
+            <DetailOrder />
+          </PrivateRoute>
+          <PublicRoute exact path={'/authorization'} restricted>
+            <Goods />
+            {/* <PublicRoute exact path={'/authorization/login'} restricted>
+                 <SignInForm />
+               </PublicRoute>
+               <PublicRoute exact path={'/authorization/registration'} restricted>
+                 <RegistrationForm />
+               </PublicRoute> */}
+          </PublicRoute>
         </Switch>
         {isLoading && <Loader />}
         <ToastContainer

+ 5 - 0
src/components/Authorization/Authorization.module.css

@@ -0,0 +1,5 @@
+.authorizationWrapper {
+  display: flex;
+  justify-content: center;
+  padding: 20px 0;
+}

+ 13 - 0
src/components/Authorization/Authorization.tsx

@@ -0,0 +1,13 @@
+import s from './Authorization.module.css';
+import Button from '../Navigation/Button/Button';
+
+const Authorization = () => {
+  return (
+    <div className={s.authorizationWrapper}>
+      <Button text="Login" to="/authorization/login" />
+      <Button text="Registration" to="/authorization/registration" />
+    </div>
+  );
+};
+
+export default Authorization;

+ 53 - 0
src/components/Authorization/Registration/RegistrationForm.module.css

@@ -0,0 +1,53 @@
+.registration_form {
+  width: 360px;
+  min-height: 106px;
+  display: flex;
+  height: auto;
+  background-color: rgb(194, 192, 196);
+  margin: 0 auto;
+  -webkit-box-shadow: 0px -23px 30px 18px #7c3ee0;
+  box-shadow: 0px -23px 30px 18px #858586;
+  border: solid 3px rgb(24, 24, 24);
+  border-radius: 20px;
+  padding-bottom: 10px;
+  padding-left: 6px;
+  padding-top: 3px;
+  padding-right: 2px;
+  border-bottom: none;
+  margin-top: 100px;
+}
+
+.registration_input_wrapper {
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  padding-right: 10px;
+  width: 280px;
+}
+
+.registration_button {
+  text-orientation: upright;
+  writing-mode: vertical-rl;
+  transform: rotate(7deg);
+  -ms-transform: rotate(7deg);
+  -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.93969262, M12=0.34202014, M21=-0.34202014, M22=0.93969262,sizingMethod='auto expand')"; /* IE6-8 */
+  -webkit-transform: rotate(7deg);
+  width: 64px;
+  height: auto;
+}
+
+.registration_show_password {
+  display: inline;
+  margin-right: 10px;
+}
+
+@media (min-width: 767px) {
+  .registration_form {
+    width: 600px;
+  }
+
+  .registration_input_wrapper {
+    padding-right: 10px;
+    width: 550px;
+  }
+}

+ 80 - 0
src/components/Authorization/Registration/RegistrationForm.tsx

@@ -0,0 +1,80 @@
+import { useFormik } from 'formik';
+import Button from '@material-ui/core/Button';
+import TextField from '@material-ui/core/TextField';
+import { connect } from 'react-redux';
+
+import { validationSchemaRegistration } from '../helpers/validation';
+import togglePassword from '../helpers/helpers';
+import { asyncCreateUser } from '../../../redux/authorization/operation';
+import {
+  IRegistrationProps,
+  IRegistrationState,
+} from '../../../typescript/registration/interfaces';
+import s from './RegistrationForm.module.css';
+
+const RegistrationForm = ({ isLoading, createUser }: IRegistrationProps) => {
+  const formik = useFormik({
+    initialValues: {
+      email: '',
+      password: '',
+    },
+    validationSchema: validationSchemaRegistration,
+    onSubmit: ({ email: login, password }) => createUser(login, password),
+  });
+
+  return (
+    <div>
+      <form className={s.registration_form} onSubmit={formik.handleSubmit}>
+        <div className={s.registration_input_wrapper}>
+          <TextField
+            id="email"
+            name="email"
+            label="Email..."
+            value={formik.values.email}
+            onChange={formik.handleChange}
+            error={formik.touched.email && Boolean(formik.errors.email)}
+            helperText={formik.touched.email && formik.errors.email}
+          />
+          <TextField
+            id="password"
+            name="password"
+            label="Password..."
+            type="password"
+            value={formik.values.password}
+            onChange={formik.handleChange}
+            error={formik.touched.password && Boolean(formik.errors.password)}
+            helperText={formik.touched.password && formik.errors.password}
+          />
+          <div>
+            <input
+              className={s.registration_show_password}
+              type="checkbox"
+              onClick={togglePassword}
+            />
+            Show password*
+          </div>
+        </div>
+        <Button
+          className={s.registration_button}
+          color="primary"
+          variant="contained"
+          disabled={isLoading ? true : false}
+          type="submit"
+        >
+          Submit
+        </Button>
+      </form>
+    </div>
+  );
+};
+
+const mapStateToProps = (state: IRegistrationState) => ({
+  isLoading: state.isLoading.flag,
+});
+
+const mapDispatchToProps = (dispatch: any) => ({
+  createUser: (login: string, password: string) =>
+    dispatch(asyncCreateUser(login, password)),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(RegistrationForm);

+ 54 - 0
src/components/Authorization/SignIn/SignIn.module.css

@@ -0,0 +1,54 @@
+.signIn_form {
+  width: 360px;
+  min-height: 106px;
+  display: flex;
+  height: auto;
+  background-color: rgb(164, 162, 167);
+  margin: 0 auto;
+  -webkit-box-shadow: 0px -23px 30px 18px #bebbc4;
+  box-shadow: 0px -23px 30px 18px #c1c0c2;
+  border: solid 3px rgb(2, 2, 2);
+  border-radius: 20px;
+  padding-bottom: 10px;
+  padding-left: 6px;
+  padding-top: 3px;
+  padding-right: 2px;
+  border-bottom: none;
+  margin-top: 100px;
+}
+.signIn_input {
+  width: 100%;
+}
+.signIn_input_wrapper {
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  padding-right: 10px;
+  width: 280px;
+}
+.signIn_button {
+  text-orientation: upright;
+  writing-mode: vertical-rl;
+  transform: rotate(7deg);
+  -ms-transform: rotate(7deg);
+  -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.93969262, M12=0.34202014, M21=-0.34202014, M22=0.93969262,sizingMethod='auto expand')"; /* IE6-8 */
+  -webkit-transform: rotate(7deg);
+  width: 64px;
+  height: auto;
+}
+
+.input_show_password {
+  display: inline-block;
+  margin-right: 10px;
+}
+
+@media (min-width: 767px) {
+  .signIn_form {
+    width: 600px;
+  }
+
+  .signIn_input_wrapper {
+    padding-right: 10px;
+    width: 550px;
+  }
+}

+ 80 - 0
src/components/Authorization/SignIn/SignIn.tsx

@@ -0,0 +1,80 @@
+import { useFormik } from 'formik';
+import Button from '@material-ui/core/Button';
+import TextField from '@material-ui/core/TextField';
+import { connect } from 'react-redux';
+
+import { validationSchemaSignIn } from '../helpers/validation';
+import { asyncLogin } from '../../../redux/authorization/operation';
+import {
+  ILoginProps,
+  ILoginState,
+} from '../../../typescript/registration/interfaces';
+import togglePassword from '../helpers/helpers';
+import s from './SignIn.module.css';
+
+const SignInForm = ({ isLoading, doLogin }: ILoginProps) => {
+  const formik = useFormik({
+    initialValues: {
+      email: '',
+      password: '',
+    },
+    validationSchema: validationSchemaSignIn,
+    onSubmit: ({ email: login, password }) => doLogin(login, password),
+  });
+
+  return (
+    <form className={s.signIn_form} onSubmit={formik.handleSubmit}>
+      <div className={s.signIn_input_wrapper}>
+        <TextField
+          className={s.signIn_input}
+          id="email"
+          name="email"
+          label="Email..."
+          value={formik.values.email}
+          onChange={formik.handleChange}
+          error={formik.touched.email && Boolean(formik.errors.email)}
+          helperText={formik.touched.email && formik.errors.email}
+        />
+        <div>
+          <TextField
+            className={s.signIn_input}
+            id="password"
+            name="password"
+            label="Password..."
+            type="password"
+            value={formik.values.password}
+            onChange={formik.handleChange}
+            error={formik.touched.password && Boolean(formik.errors.password)}
+            helperText={formik.touched.password && formik.errors.password}
+          />
+          <input
+            className={s.input_show_password}
+            type="checkbox"
+            onClick={togglePassword}
+          />
+          Show password*
+        </div>
+      </div>
+      <Button
+        className={s.signIn_button}
+        color="primary"
+        variant="contained"
+        type="submit"
+        disabled={isLoading ? true : false}
+      >
+        Submit
+      </Button>
+    </form>
+  );
+};
+
+const mapStateToProps = (state: ILoginState) => ({
+  isLoading: state.isLoading.flag,
+});
+
+const mapDispatchToProps = (dispatch: any) => ({
+  doLogin: (login: string, password: string) =>
+    dispatch(asyncLogin(login, password)),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(SignInForm);

+ 10 - 0
src/components/Authorization/helpers/helpers.ts

@@ -0,0 +1,10 @@
+const togglePassword = () => {
+  let el = document.getElementById('password') as HTMLInputElement;
+  if (el.type === 'password') {
+    el.type = 'text';
+  } else {
+    el.type = 'password';
+  }
+};
+
+export default togglePassword;

+ 27 - 0
src/components/Authorization/helpers/validation.js

@@ -0,0 +1,27 @@
+import * as yup from 'yup';
+
+const validationSchemaRegistration = yup.object({
+  email: yup
+    .string('Enter your email')
+    .email('Enter a valid email')
+    .required('Email is required'),
+  password: yup
+    .string('Enter your password')
+    .min(8, 'Password should be of minimum 8 characters length')
+    .max(20, 'Password should be less 20 characters')
+    .required('Password is required'),
+});
+
+const validationSchemaSignIn = yup.object({
+  email: yup
+    .string('Enter your email')
+    .email('Enter a valid email')
+    .required('Email is required'),
+  password: yup
+    .string('Enter your password')
+    .min(8, 'Password should be of minimum 8 characters length')
+    .max(20, 'Password should be less 20 characters')
+    .required('Password is required'),
+});
+
+export { validationSchemaRegistration, validationSchemaSignIn };

+ 1 - 1
src/components/Categories/Categories.tsx

@@ -26,7 +26,7 @@ const Categories = ({ categoriesArr, getCategories }: ICategoriesProps) => {
         {categoriesArr.length !== 0 &&
           categoriesArr
             .slice((page - 1) * 5, page * 5)
-            .map((category: any, i: number) => (
+            .map((category, i: number) => (
               <Category key={i} category={category} />
             ))}
       </ul>

+ 2 - 6
src/components/Categories/Category/Category.tsx

@@ -2,13 +2,9 @@ import React from 'react';
 import { Link } from 'react-router-dom';
 
 import s from './Category.module.css';
-import { ICategory } from '../../../typescript/categories/interfaces';
+import { ICategoryItem } from '../../../typescript/categories/interfaces';
 
-const Category = ({
-  category: { _id, name, createdAt },
-}: {
-  category: ICategory;
-}) => {
+const Category = ({ category: { _id, name, createdAt } }: ICategoryItem) => {
   return (
     <li className={s.category_item}>
       <Link className={s.category_link} to={`/categories/goods/${name}`}>

+ 4 - 2
src/components/Goods/DetailGood/DetailGood.tsx

@@ -5,9 +5,11 @@ import { useRouteMatch } from 'react-router-dom';
 import s from './DetailGood.module.css';
 import { goodById, makeOrderById } from '../../../api-data';
 import { IGoodDetail } from '../../../typescript/goods/interfaces';
-import { TRouteMatchId } from '../../../typescript/goods/types';
+import {
+  TRouteMatchId,
+  TDispatchLoading,
+} from '../../../typescript/goods/types';
 import { actionLoading } from '../../../redux/loading/action';
-import { TDispatchLoading } from '../../../typescript/goods/types';
 
 function DetailGood({ dispatchLoading }: TDispatchLoading) {
   const {

+ 1 - 1
src/components/Goods/Goods.tsx

@@ -28,7 +28,7 @@ const Goods = ({ goodsArr, getGoods }: IGoodsProps) => {
         {goodsArr.length !== 0 &&
           goodsArr
             .slice((page - 1) * 5, page * 5)
-            .map((good: any, i: number) => (
+            .map((good, i: number) => (
               <Good key={i} good={good} routName={routName} />
             ))}
       </ul>

+ 14 - 12
src/components/Navigation/Navigation.tsx

@@ -1,24 +1,26 @@
-import { useDispatch } from 'react-redux';
-
 import Button from './Button/Button';
-// import { asyncLogout } from '../../redux/authorization/operations';
-
+import { connect } from 'react-redux';
+import { asyncLogout } from '../../redux/authorization/operation';
+import { INavigationProps } from '../../typescript/navigation/interfaces';
 import s from './Navigation.module.css';
 
-const Navigation = () => {
-  // const dispatch = useDispatch();
-  // const handleLogout = async () => dispatch(asyncLogout())
-
+const Navigation = ({ doLogout }: INavigationProps) => {
+  const handleLogout = async () => doLogout();
   return (
     <div className={s.wrapper__button}>
       <Button text="Categories" to="/categories" />
       <Button text="Orders" to="/orders" />
-      {/* <div className={s.wrapper__button__logout}>
+      <div className={s.wrapper__button__logout}>
         <div onClick={handleLogout}>
-          <Button  text="Logout" to="/authorization" />
+          <Button text="Logout" to="/authorization" />
         </div>
-      </div> */}
+      </div>
     </div>
   );
 };
-export default Navigation;
+
+const mapDispatchToProps = (dispatch: any) => ({
+  doLogout: () => dispatch(asyncLogout()),
+});
+
+export default connect(null, mapDispatchToProps)(Navigation);

+ 15 - 0
src/components/Orders/DetailOrder/DetailOrder.module.css

@@ -0,0 +1,15 @@
+.detailOrder_wrapper {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
+  padding: 10px;
+  padding-top: 60px;
+  color: white;
+  font-size: 24px;
+}
+
+@media (min-width: 767px) {
+}
+@media (min-width: 1400px) {
+}

+ 47 - 0
src/components/Orders/DetailOrder/DetailOrder.tsx

@@ -0,0 +1,47 @@
+import { useEffect, useState } from 'react';
+import { useRouteMatch } from 'react-router-dom';
+import { connect } from 'react-redux';
+
+import s from './DetailOrder.module.css';
+import { actionLoading } from '../../../redux/loading/action';
+import { orderById } from '../../../api-data';
+import { IOrderDetail } from '../../../typescript/orders/interfaces';
+import {
+  TRouteMatchId,
+  TDispatchLoading,
+} from '../../../typescript/orders/types';
+
+function DetailOrder({ dispatchLoading }: TDispatchLoading) {
+  const {
+    params: { id },
+  }: TRouteMatchId = useRouteMatch();
+
+  const [order, setOrder] = useState<IOrderDetail | null>(null);
+
+  useEffect(() => {
+    async function getGood() {
+      try {
+        dispatchLoading(true);
+        const data = await orderById<IOrderDetail>(id);
+        setOrder(data);
+      } catch (e) {
+        console.error(e);
+      } finally {
+        dispatchLoading(false);
+      }
+    }
+    getGood();
+  }, [dispatchLoading, id]);
+
+  return order ? (
+    <div className={s.detailOrder_wrapper}>
+      <p>_id : {order._id ? order._id : 'missed'}</p>
+      <p>createdAt : {order.createdAt ? order.createdAt : 'missed'}</p>
+    </div>
+  ) : null;
+}
+const mapDispatchToProps = (dispatch: any) => ({
+  dispatchLoading: (value: boolean) => dispatch(actionLoading(value)),
+});
+
+export default connect(null, mapDispatchToProps)(DetailOrder);

+ 15 - 0
src/components/Orders/Order/Order.module.css

@@ -0,0 +1,15 @@
+.order_item {
+  position: relative;
+  margin-bottom: 10px;
+}
+
+.order_link {
+  text-decoration: none;
+  color: white;
+}
+
+@media (min-width: 767px) {
+}
+
+@media (min-width: 1400px) {
+}

+ 20 - 0
src/components/Orders/Order/Order.tsx

@@ -0,0 +1,20 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+
+import s from './Order.module.css';
+import { IOrderItem } from '../../../typescript/orders/interfaces';
+
+const Order = ({
+  order: { _id, total, createdAt, orderGoods },
+}: IOrderItem) => {
+  return (
+    <li className={s.order_item}>
+      <Link className={s.order_link} to={`/orders/${_id}`}>
+        <p>total : {total ? total : 'missed'}</p>
+        <p>createdAt : {createdAt ? createdAt : 'missed'}</p>
+        <p>count : {orderGoods[0].count ? orderGoods[0].count : 'missed'}</p>
+      </Link>
+    </li>
+  );
+};
+export default Order;

+ 16 - 0
src/components/Orders/Orders.module.css

@@ -0,0 +1,16 @@
+.orders_list {
+  font-family: 'Mountains of Christmas', cursive;
+  list-style: none;
+  display: flex;
+  justify-content: space-around;
+  flex-wrap: wrap;
+  padding-bottom: 10px;
+  padding-top: 200px;
+  padding-left: 0;
+}
+
+@media (min-width: 767px) {
+}
+
+@media (min-width: 1400px) {
+}

+ 46 - 0
src/components/Orders/Orders.tsx

@@ -0,0 +1,46 @@
+import { useEffect, useState } from 'react';
+import { Route } from 'react-router-dom';
+import { connect } from 'react-redux';
+
+import s from './Orders.module.css';
+import { asyncGetOrders } from '../../redux/orders/operation';
+import { IOrdersProps, IOrdersState } from '../../typescript/orders/interfaces';
+
+import Order from './Order/Order';
+import Pagination from '../PaginationBar/PaginationBar';
+
+const Orders = ({ ordersArr, getOrders }: IOrdersProps) => {
+  const [page, setPage] = useState<number>(1);
+
+  const handlePageChange = (page: number) => setPage(page);
+
+  useEffect(() => {
+    getOrders();
+  }, [getOrders]);
+
+  return ordersArr ? (
+    <Route>
+      <ul className={s.orders_list}>
+        {ordersArr.length !== 0 &&
+          ordersArr
+            .slice((page - 1) * 5, page * 5)
+            .map((order, i: number) => <Order key={i} order={order} />)}
+      </ul>
+      <Pagination
+        page={page}
+        total={Math.ceil(ordersArr.length / 5)}
+        handlePageChange={handlePageChange}
+      />
+    </Route>
+  ) : null;
+};
+
+const mapStateToProps = (state: IOrdersState) => ({
+  ordersArr: state.orders.ordersArr,
+});
+
+const mapDispatchToProps = (dispatch: any) => ({
+  getOrders: () => dispatch(asyncGetOrders()),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Orders);

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

@@ -0,0 +1,21 @@
+import { Route, Redirect } from 'react-router-dom';
+import { connect } from 'react-redux';
+
+import {
+  IPrivateState,
+  IPrivateProps,
+} from '../../../typescript/routes/interfaces';
+
+function PrivateRoute({ children, token, ...routeProps }: IPrivateProps) {
+  return (
+    <Route {...routeProps}>
+      {token ? children : <Redirect to="/authorization" />}
+    </Route>
+  );
+}
+
+const mapStateToProps = (state: IPrivateState) => ({
+  token: state.authorization.token,
+});
+
+export default connect(mapStateToProps, null)(PrivateRoute);

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

@@ -0,0 +1,28 @@
+import { Route, Redirect } from 'react-router-dom';
+import { connect } from 'react-redux';
+
+import {
+  IPublicState,
+  IPublicProps,
+} from '../../../typescript/routes/interfaces';
+
+function PublicRoute({
+  children,
+  restricted = false,
+  token,
+  ...routeProps
+}: IPublicProps) {
+  const shouldRedirect = token && restricted;
+
+  return (
+    <Route {...routeProps}>
+      {shouldRedirect ? <Redirect to="/" /> : children}
+    </Route>
+  );
+}
+
+const mapStateToProps = (state: IPublicState) => ({
+  token: state.authorization.token,
+});
+
+export default connect(mapStateToProps, null)(PublicRoute);

+ 0 - 12
src/index.tsx

@@ -2,7 +2,6 @@ import React, { Suspense } from 'react';
 import { Provider } from 'react-redux';
 import ReactDOM from 'react-dom';
 import 'react-toastify/dist/ReactToastify.css';
-import { ToastContainer } from 'react-toastify';
 
 import 'modern-normalize/modern-normalize.css';
 import './index.css';
@@ -14,17 +13,6 @@ ReactDOM.render(
     <Suspense fallback={null}>
       <Provider store={store}>
         <App />
-        <ToastContainer
-          position="top-right"
-          autoClose={3000}
-          hideProgressBar={false}
-          newestOnTop={false}
-          closeOnClick
-          rtl={false}
-          pauseOnFocusLoss
-          draggable
-          pauseOnHover
-        />
       </Provider>
     </Suspense>
   </React.StrictMode>,

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

@@ -0,0 +1,29 @@
+import { IAuthorizationPayload } from './../../../typescript/reduxTs/authorization/interfaces';
+import { authorizationActionType } from '../actionType';
+
+const actionLoginSuccess = (payload: IAuthorizationPayload) => ({
+  type: authorizationActionType.loginSuccess,
+  payload: payload,
+});
+
+const actionLoginReject = () => ({
+  type: authorizationActionType.logoutReject,
+  payload: {},
+});
+
+const actionLogoutSuccess = (payload: IAuthorizationPayload) => ({
+  type: authorizationActionType.logoutSuccess,
+  payload: payload,
+});
+
+const actionLogoutReject = () => ({
+  type: authorizationActionType.logoutReject,
+  payload: {},
+});
+
+export {
+  actionLoginSuccess,
+  actionLoginReject,
+  actionLogoutSuccess,
+  actionLogoutReject,
+};

+ 6 - 0
src/redux/authorization/actionType/index.ts

@@ -0,0 +1,6 @@
+export const authorizationActionType = {
+  loginSuccess: 'login/success',
+  loginReject: 'login/reject',
+  logoutSuccess: 'logout/success',
+  logoutReject: 'logout/reject',
+};

+ 54 - 0
src/redux/authorization/operation/index.ts

@@ -0,0 +1,54 @@
+import {
+  actionLoginSuccess,
+  actionLoginReject,
+  actionLogoutSuccess,
+  actionLogoutReject,
+} from '../action';
+import { actionLoading } from '../../loading/action';
+import { loginGQL, registerGQL } from '../../../api-data';
+
+const asyncLogin = (login: string, password: string) => async (
+  dispatch: any,
+) => {
+  try {
+    dispatch(actionLoading(true));
+    const token = await loginGQL<string>(login, password);
+    if (!token) throw new Error('Wrong credentials');
+    localStorage.token = token;
+    dispatch(actionLoginSuccess({ login, token }));
+  } catch (e) {
+    dispatch(actionLoginReject());
+  } finally {
+    dispatch(actionLoading(false));
+  }
+};
+
+const asyncLogout = () => async (dispatch: any) => {
+  try {
+    dispatch(actionLoading(true));
+    dispatch(actionLogoutSuccess({ login: '', token: '' }));
+    localStorage.token = null;
+  } catch (e) {
+    dispatch(actionLogoutReject());
+  } finally {
+    dispatch(actionLoading(false));
+  }
+};
+
+const asyncCreateUser = (login: string, password: string) => async (
+  dispatch: any,
+) => {
+  try {
+    dispatch(actionLoading(true));
+    await registerGQL(login, password);
+    const token = await loginGQL<string>(login, password);
+    if (!token) throw new Error('Server error');
+    dispatch(actionLoginSuccess({ login, token }));
+  } catch (e) {
+    dispatch(actionLoginReject());
+  } finally {
+    dispatch(actionLoading(false));
+  }
+};
+
+export { asyncLogin, asyncLogout, asyncCreateUser };

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

@@ -0,0 +1,25 @@
+import { authorizationActionType } from '../actionType';
+import { IAuthorizationReducer } from '../../../typescript/reduxTs/authorization/interfaces';
+
+export const initialState = {
+  login: '',
+  token: '',
+};
+
+export function authorizationReducer(
+  state = initialState,
+  { type, payload }: IAuthorizationReducer,
+) {
+  switch (type) {
+    case authorizationActionType.loginSuccess:
+      return payload;
+    case authorizationActionType.loginReject:
+      return state;
+    case authorizationActionType.logoutSuccess:
+      return payload;
+    case authorizationActionType.logoutReject:
+      return state;
+    default:
+      return state;
+  }
+}

+ 6 - 1
src/redux/categories/operation/index.ts

@@ -11,7 +11,12 @@ const asyncGetCategories = () => async (dispatch: any) => {
     dispatch(actionLoading(true));
 
     const data = await categoriesGQL<ICategory[]>();
-    data && dispatch(actionGetCategoriesSusses(data));
+
+    if (data && data.length > 0) {
+      dispatch(actionGetCategoriesSusses(data));
+    } else {
+      throw new Error('Wrong request');
+    }
   } catch (e) {
     dispatch(actionGetCategoriesReject());
   } finally {

+ 5 - 1
src/redux/goods/operation/index.ts

@@ -7,7 +7,11 @@ const asyncGetGoods = () => async (dispatch: any) => {
   try {
     dispatch(actionLoading(true));
     const data = await goodsGQL<IGood[]>();
-    data && dispatch(actionGetGoodsSusses(data));
+    if (data && data.length > 0) {
+      dispatch(actionGetGoodsSusses(data));
+    } else {
+      throw new Error('Wrong request');
+    }
   } catch (e) {
     dispatch(actionGetGoodsReject());
   } finally {

+ 14 - 0
src/redux/orders/action/index.ts

@@ -0,0 +1,14 @@
+import { ordersActionType } from '../actionType';
+import { IOrder } from '../../../typescript/orders/interfaces';
+
+const actionGetOrdersSusses = (arr: IOrder[]) => ({
+  type: ordersActionType.success,
+  payload: arr,
+});
+
+const actionGetOrdersReject = () => ({
+  type: ordersActionType.reject,
+  payload: {},
+});
+
+export { actionGetOrdersSusses, actionGetOrdersReject };

+ 4 - 0
src/redux/orders/actionType/index.ts

@@ -0,0 +1,4 @@
+export const ordersActionType = {
+  success: 'orders/success',
+  reject: 'orders/reject',
+};

+ 22 - 0
src/redux/orders/operation/index.ts

@@ -0,0 +1,22 @@
+import { actionGetOrdersSusses, actionGetOrdersReject } from '../action';
+import { actionLoading } from '../../loading/action';
+import { ordersGQL } from '../../../api-data';
+import { IOrder } from '../../../typescript/orders/interfaces';
+
+const asyncGetOrders = () => async (dispatch: any) => {
+  try {
+    dispatch(actionLoading(true));
+    const data = await ordersGQL<IOrder[]>();
+    if (data && data.length > 0) {
+      dispatch(actionGetOrdersSusses(data));
+    } else {
+      throw new Error('Wrong request');
+    }
+  } catch (e) {
+    dispatch(actionGetOrdersReject());
+  } finally {
+    dispatch(actionLoading(false));
+  }
+};
+
+export { asyncGetOrders };

+ 22 - 0
src/redux/orders/reducer/index.ts

@@ -0,0 +1,22 @@
+import { ordersActionType } from '../actionType';
+import { IOrdersReducer } from '../../../typescript/reduxTs/orders/interfaces';
+
+const initialState = {
+  ordersArr: [],
+};
+
+export function ordersReducer(
+  state = initialState,
+  { type, payload }: IOrdersReducer,
+) {
+  switch (type) {
+    case ordersActionType.success:
+      return {
+        ordersArr: payload,
+      };
+    case ordersActionType.reject:
+      return state;
+    default:
+      return state;
+  }
+}

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

@@ -1,10 +1,14 @@
 import { combineReducers } from 'redux';
 import { categoriesReducer } from '../categories/reducer';
 import { goodsReducer } from '../goods/reducer';
+import { ordersReducer } from '../orders/reducer';
+import { authorizationReducer } from '../authorization/reducer';
 import { LoadingReducer } from '../loading/reducer';
 
 export const rootReducer = combineReducers({
   categories: categoriesReducer,
   goods: goodsReducer,
+  orders: ordersReducer,
+  authorization: authorizationReducer,
   isLoading: LoadingReducer,
 });

+ 4 - 0
src/typescript/categories/interfaces.ts

@@ -4,6 +4,10 @@ export interface ICategory {
   createdAt: string | undefined;
 }
 
+export interface ICategoryItem {
+  category: ICategory;
+}
+
 export interface ICategoriesProps {
   categoriesArr: ICategory[] | [];
   getCategories: () => void;

+ 3 - 0
src/typescript/navigation/interfaces.ts

@@ -0,0 +1,3 @@
+export interface INavigationProps {
+  doLogout: () => void;
+}

+ 40 - 0
src/typescript/orders/interfaces.ts

@@ -0,0 +1,40 @@
+export interface IOrder {
+  _id: string | undefined;
+  total?: number | undefined;
+  createdAt: string | undefined;
+  orderGoods: [
+    {
+      count: number | undefined;
+    },
+  ];
+}
+
+export interface IOrderItem {
+  order: IOrder;
+}
+
+export interface IOrderDetail {
+  _id: string | undefined;
+  name?: string | undefined;
+  createdAt: string | undefined;
+  price?: number;
+  images?:
+    | [
+        {
+          url: string;
+        },
+      ]
+    | null;
+  description: string | undefined;
+}
+
+export interface IOrdersProps {
+  ordersArr: IOrder[] | [];
+  getOrders: () => void;
+}
+
+export interface IOrdersState {
+  orders: {
+    ordersArr: IOrder[];
+  };
+}

+ 9 - 0
src/typescript/orders/types.ts

@@ -0,0 +1,9 @@
+export type TRouteMatchId = {
+  params: {
+    id: string;
+  };
+};
+
+export type TDispatchLoading = {
+  dispatchLoading: (value: boolean) => any;
+};

+ 6 - 0
src/typescript/reduxTs/authorization/enum.ts

@@ -0,0 +1,6 @@
+export enum ETypes {
+  loginSuccess = 'login/success',
+  loginReject = 'login/reject',
+  logoutSuccess = 'logout/success',
+  logoutReject = 'logout/reject',
+}

+ 23 - 0
src/typescript/reduxTs/authorization/interfaces.ts

@@ -0,0 +1,23 @@
+import { ETypes } from './enum';
+
+export interface IToken {
+  token: string;
+}
+
+export interface ILogin {
+  login: string;
+}
+
+export interface IAuthorizationPayload {
+  login: string;
+  token: string;
+}
+
+export interface IAuthorizationReducer {
+  type:
+    | ETypes.loginSuccess
+    | ETypes.logoutReject
+    | ETypes.logoutSuccess
+    | ETypes.logoutReject;
+  payload: IAuthorizationPayload;
+}

+ 4 - 0
src/typescript/reduxTs/orders/enum.ts

@@ -0,0 +1,4 @@
+export enum ETypes {
+  success = 'orders/success',
+  reject = 'orders/reject',
+}

+ 7 - 0
src/typescript/reduxTs/orders/interfaces.ts

@@ -0,0 +1,7 @@
+import { IOrder } from '../../orders/interfaces';
+import { ETypes } from './enum';
+
+export interface IOrdersReducer {
+  type: ETypes.reject | ETypes.success;
+  payload: IOrder[];
+}

+ 21 - 0
src/typescript/registration/interfaces.ts

@@ -0,0 +1,21 @@
+export interface IRegistrationProps {
+  isLoading: boolean;
+  createUser: (login: string, password: string) => void;
+}
+
+export interface IRegistrationState {
+  isLoading: {
+    flag: boolean;
+  };
+}
+
+export interface ILoginProps {
+  isLoading: boolean;
+  doLogin: (login: string, password: string) => void;
+}
+
+export interface ILoginState {
+  isLoading: {
+    flag: boolean;
+  };
+}

+ 26 - 0
src/typescript/routes/interfaces.ts

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