Jelajahi Sumber

done some logic with connect and typescrypt

unknown 2 tahun lalu
induk
melakukan
fbdd3c3787
42 mengubah file dengan 927 tambahan dan 27 penghapusan
  1. 1 1
      .eslintcache
  2. 1 1
      package.json
  3. 1 0
      src/App.module.css
  4. 39 4
      src/App.tsx
  5. 211 0
      src/api-data/index.ts
  6. 16 0
      src/components/Categories/Categories.module.css
  7. 50 0
      src/components/Categories/Categories.tsx
  8. 15 0
      src/components/Categories/Category/Category.module.css
  9. 22 0
      src/components/Categories/Category/Category.tsx
  10. 23 0
      src/components/Goods/DetailGood/DetailGood.module.css
  11. 49 0
      src/components/Goods/DetailGood/DetailGood.tsx
  12. 17 0
      src/components/Goods/Good/Good.module.css
  13. 29 0
      src/components/Goods/Good/Good.tsx
  14. 10 0
      src/components/Goods/Goods.module.css
  15. 53 0
      src/components/Goods/Goods.tsx
  16. 0 0
      src/components/Just/Just.module.css
  17. 0 9
      src/components/Just/Just.tsx
  18. 50 0
      src/components/Navigation/Button/Button.module.css
  19. 26 0
      src/components/Navigation/Button/Button.tsx
  20. 19 0
      src/components/Navigation/Navigation.module.css
  21. 24 0
      src/components/Navigation/Navigation.tsx
  22. 22 0
      src/components/PaginationBar/PaginationBar.jsx
  23. 6 0
      src/components/PaginationBar/PaginationBar.module.css
  24. 16 12
      src/index.tsx
  25. 14 0
      src/redux/categories/action/index.ts
  26. 4 0
      src/redux/categories/actionType/index.ts
  27. 17 0
      src/redux/categories/operation/index.ts
  28. 22 0
      src/redux/categories/reducer/index.ts
  29. 14 0
      src/redux/goods/action/index.ts
  30. 4 0
      src/redux/goods/actionType/index.ts
  31. 14 0
      src/redux/goods/operation/index.ts
  32. 22 0
      src/redux/goods/reducer/index.ts
  33. 8 0
      src/redux/rootReducer/index.js
  34. 10 0
      src/redux/store/index.js
  35. 16 0
      src/typescript/categories/interfaces.ts
  36. 5 0
      src/typescript/categories/types.ts
  37. 44 0
      src/typescript/goods/interfaces.ts
  38. 11 0
      src/typescript/goods/types.ts
  39. 4 0
      src/typescript/reduxTs/categories/enum.ts
  40. 7 0
      src/typescript/reduxTs/categories/interfaces.ts
  41. 4 0
      src/typescript/reduxTs/goods/enum.ts
  42. 7 0
      src/typescript/reduxTs/goods/interfaces.ts

File diff ditekan karena terlalu besar
+ 1 - 1
.eslintcache


+ 1 - 1
package.json

@@ -1,5 +1,5 @@
 {
-  "homepage": "https://github.com/grisha19961116/Helena",
+  "homepage": "http://localhost:3007/",
   "name": "react-21-22",
   "version": "0.1.0",
   "private": true,

+ 1 - 0
src/App.module.css

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

+ 39 - 4
src/App.tsx

@@ -1,9 +1,31 @@
 import { lazy } from 'react';
+import { BrowserRouter, Switch, Route } from 'react-router-dom';
 
 import s from './App.module.css';
 
-const CountriesList = lazy(
-  () => import('./components/Just/Just' /* webpackChunkName: "Just" */),
+const Navigation = lazy(
+  () =>
+    import(
+      './components/Navigation/Navigation' /* webpackChunkName: "Navigation" */
+    ),
+);
+
+const Categories = lazy(
+  () =>
+    import(
+      './components/Categories/Categories' /* webpackChunkName: "Categories" */
+    ),
+);
+
+const Goods = lazy(
+  () => import('./components/Goods/Goods' /* webpackChunkName: "Goods" */),
+);
+
+const DetailGood = lazy(
+  () =>
+    import(
+      './components/Goods/DetailGood/DetailGood' /* webpackChunkName: "DetailGood" */
+    ),
 );
 
 const Loader = lazy(
@@ -13,8 +35,21 @@ const Loader = lazy(
 function App() {
   return (
     <div className={s.appWrapper}>
-      <CountriesList />
-      <Loader />
+      <BrowserRouter>
+        <Navigation />
+        <Switch>
+          <Route exact path="/categories">
+            <Categories />
+          </Route>
+          <Route exact path="/categories/goods/:name">
+            <Goods />
+          </Route>
+          <Route exact path="/categories/goods/:name/:id">
+            <DetailGood />
+          </Route>
+          <Route></Route>
+        </Switch>
+      </BrowserRouter>
     </div>
   );
 }

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

@@ -0,0 +1,211 @@
+const getGQL = (url: string) => async (query: string, variables: any) => {
+  try {
+    const token: string = localStorage.token ? localStorage.token : '';
+    const res = await fetch(url, {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+        Authorization: 'Bearer ' + token,
+      },
+      body: JSON.stringify({ query, variables }),
+    });
+    return res.json();
+  } catch (e) {
+    return e;
+  }
+};
+
+const gql = getGQL('http://shop-roles.asmer.fs.a-level.com.ua/graphql');
+
+const loginGQL = async <T>(
+  login: string,
+  password: string,
+): Promise<T | null> => {
+  try {
+    const { data } = await gql(
+      `
+query log($login:String, $password:String){
+        login(login:$login, password:$password)
+    }`,
+      { login, password },
+    );
+    const token = data.login;
+    if (token) localStorage.token = token;
+    return token;
+  } catch (e) {
+    return null;
+  }
+};
+
+const registerGQL = async <T>(
+  login: string,
+  password: string,
+): Promise<T | null> => {
+  try {
+    const { data } = await gql(
+      `mutation register($login:String, $password:String){
+        UserUpsert(user: {login:$login, password:$password}){
+			_id,login
+		}
+    }`,
+      { login, password },
+    );
+    return data.UserUpsert;
+  } catch (e) {
+    return null;
+  }
+};
+
+const categoriesGQL = async <T>(): Promise<T | null> => {
+  try {
+    const { data } = await gql(
+      `query allCategories{
+		CategoryFind(query:"[{}]"){
+          _id,
+          name,
+		  createdAt
+		}
+	}`,
+      {},
+    );
+    return data.CategoryFind;
+  } catch (e) {
+    return null;
+  }
+};
+
+const goodsGQL = async <T>(): Promise<T | null> => {
+  try {
+    const { data } = await gql(
+      `query allGoods{
+		GoodFind(query:"[{}]"){
+          _id
+          name,
+		  createdAt,
+		  price,
+      images  {
+				url
+			}
+		}
+	}`,
+      {},
+    );
+    return data.GoodFind;
+  } catch (e) {
+    return null;
+  }
+};
+
+const categoryById = async <T>(_id: string): Promise<T | null> => {
+  try {
+    const { data } = await gql(
+      `query categoryById($id:String){
+		CategoryFindOne(query:$id){
+			_id,name,createdAt,
+			goods {
+				_id,createdAt,name
+			}
+		 },
+	}`,
+      { id: JSON.stringify([{ _id }]) },
+    );
+    return data.CategoryFindOne;
+  } catch (e) {
+    return null;
+  }
+};
+
+const goodById = async <T>(_id: string): Promise<T | null> => {
+  try {
+    const { data } = await gql(
+      `query findById($id:String){
+        GoodFindOne (query:$id){
+            _id createdAt name price description images  {
+				url
+			}
+        }
+    }`,
+      { id: JSON.stringify([{ _id }]) },
+    );
+
+    return data.GoodFindOne;
+  } catch (e) {
+    return null;
+  }
+};
+
+const ordersGQL = async <T>(): Promise<T | null> => {
+  try {
+    const { data } = await gql(
+      `query orders {
+    OrderFind(query:"[{}]")
+    {
+    _id
+    createdAt 
+    total
+    orderGoods {
+      price
+      count
+      total
+      good {
+        name
+      }
+    }
+  }
+}`,
+      {},
+    );
+    return data.OrderFind;
+  } catch (e) {
+    return null;
+  }
+};
+
+const orderById = async <T>(_id: string): Promise<T | null> => {
+  try {
+    const { data } = await gql(
+      `query findById($id:String){
+        OrderFindOne (query:$id){
+            _id createdAt    
+        }
+    }`,
+      { id: JSON.stringify([{ _id }]) },
+    );
+    return data.OrderFindOne;
+  } catch (e) {
+    return null;
+  }
+};
+
+const makeOrderById = async <T>(id: string): Promise<T | null> => {
+  try {
+    const { data } = await gql(
+      `  mutation makeOrder($id:ID){
+		OrderUpsert(order:{
+      orderGoods:[
+        {count:1,good:{_id:$id}}
+      ]
+    }){
+          _id,createdAt,total
+		}
+	}`,
+      { id },
+    );
+
+    return data.OrderUpsert;
+  } catch (e) {
+    return null;
+  }
+};
+
+export {
+  loginGQL,
+  registerGQL,
+  categoriesGQL,
+  goodsGQL,
+  categoryById,
+  goodById,
+  ordersGQL,
+  orderById,
+  makeOrderById,
+};

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

@@ -0,0 +1,16 @@
+.categories_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) {
+}

+ 50 - 0
src/components/Categories/Categories.tsx

@@ -0,0 +1,50 @@
+import { useEffect, useState } from 'react';
+import { Route } from 'react-router-dom';
+import { connect } from 'react-redux';
+
+import s from './Categories.module.css';
+import { asyncGetCategories } from '../../redux/categories/operation';
+import {
+  ICategoriesProps,
+  ICategoriesState,
+} from '../../typescript/categories/interfaces';
+
+import Category from './Category/Category';
+import Pagination from '../PaginationBar/PaginationBar';
+
+const Categories = ({ categoriesArr, getCategories }: ICategoriesProps) => {
+  useEffect(() => {
+    getCategories();
+  }, [getCategories]);
+
+  const [page, setPage] = useState(1);
+  const handlePageChange = (page: number) => setPage(page);
+
+  return categoriesArr ? (
+    <Route>
+      <ul className={s.categories_list}>
+        {categoriesArr.length !== 0 &&
+          categoriesArr
+            .slice((page - 1) * 5, page * 5)
+            .map((category: any, i: number) => (
+              <Category key={i} category={category} />
+            ))}
+      </ul>
+      <Pagination
+        page={page}
+        total={Math.ceil(categoriesArr.length / 5)}
+        handlePageChange={handlePageChange}
+      />
+    </Route>
+  ) : null;
+};
+
+const mapStateToProps = (state: ICategoriesState) => ({
+  categoriesArr: state.categories.categoriesArr,
+});
+
+const mapDispatchToProps = (dispatch: any) => ({
+  getCategories: () => dispatch(asyncGetCategories()),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Categories);

+ 15 - 0
src/components/Categories/Category/Category.module.css

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

+ 22 - 0
src/components/Categories/Category/Category.tsx

@@ -0,0 +1,22 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+
+import s from './Category.module.css';
+import { ICategory } from '../../../typescript/categories/interfaces';
+
+const Category = ({
+  category: { _id, name, createdAt },
+}: {
+  category: ICategory;
+}) => {
+  return (
+    <li className={s.category_item}>
+      <Link className={s.category_link} to={`/categories/goods/${name}`}>
+        <p>_id : {_id ? _id : 'missed'}</p>
+        <p>name : {name ? name : 'missed'}</p>
+        <p>createdAt : {createdAt ? createdAt : 'missed'}</p>
+      </Link>
+    </li>
+  );
+};
+export default Category;

+ 23 - 0
src/components/Goods/DetailGood/DetailGood.module.css

@@ -0,0 +1,23 @@
+.detailGood_wrapper {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
+  padding: 10px;
+  padding-top: 60px;
+  color: white;
+  font-size: 24px;
+}
+
+.detailGoodBtn {
+  margin-top: 20px;
+  background-color: rgb(16, 255, 16);
+  border-radius: 8px;
+  color: rgb(114, 114, 114);
+  width: 160px;
+}
+
+@media (min-width: 767px) {
+}
+@media (min-width: 1400px) {
+}

+ 49 - 0
src/components/Goods/DetailGood/DetailGood.tsx

@@ -0,0 +1,49 @@
+import React, { useEffect, useState } from 'react';
+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';
+
+function DetailGood() {
+  const {
+    params: { id },
+  }: TRouteMatchId = useRouteMatch();
+  const randomPrice = (min: number, max: number) =>
+    Math.floor(Math.random() * (max - min + 1) + min);
+  const price = randomPrice(1, 10);
+  const handlePurchase = async () => makeOrderById(id);
+  const [good, setGood] = useState<IGoodDetail | null>(null);
+
+  useEffect(() => {
+    goodById<IGoodDetail>(id).then(data => setGood(data));
+  }, [id]);
+
+  return good ? (
+    <div className={s.detailGood_wrapper}>
+      <p>name : {good.name ? good.name : 'missed'}</p>
+      <p>createdAt : {good.createdAt ? good.createdAt : 'missed'}</p>
+      <p>fake price for redux : {price}</p>
+      <p>price : {good.price ? good.price : 'missed'}</p>
+      <p>description : {good.description ? good.description : 'missed'}</p>
+      <img
+        src={`http://shop-roles.asmer.fs.a-level.com.ua/${
+          good.images && good.images[0].url
+        }`}
+        alt="*it has to be pic of product*"
+        width="200"
+        height="200"
+      ></img>
+      <button
+        onClick={handlePurchase}
+        className={s.detailGoodBtn}
+        type="button"
+      >
+        Purchase
+      </button>
+    </div>
+  ) : null;
+}
+
+export default DetailGood;

+ 17 - 0
src/components/Goods/Good/Good.module.css

@@ -0,0 +1,17 @@
+.good_item {
+  position: relative;
+  display: block;
+  margin-bottom: 10px;
+  width: 16%;
+  height: 300px;
+}
+
+.good_link {
+  text-decoration: none;
+  color: white;
+}
+
+@media (min-width: 767px) {
+}
+@media (min-width: 1400px) {
+}

+ 29 - 0
src/components/Goods/Good/Good.tsx

@@ -0,0 +1,29 @@
+import { Link } from 'react-router-dom';
+
+import s from './Good.module.css';
+import { IGoodItem } from '../../../typescript/goods/interfaces';
+
+const Good = ({
+  good: { _id, name, createdAt, price, images },
+  routName,
+}: IGoodItem) => {
+  return (
+    <li className={s.good_item}>
+      <Link className={s.good_link} to={`/categories/goods/${routName}/${_id}`}>
+        <p>_id : {_id}</p>
+        <p>name : {name ? name : 'missed'}</p>
+        <p>createdAt : {createdAt ? createdAt : 'missed'}</p>
+        <p>price : {price ? price : 'unknown'}</p>
+        <img
+          src={`http://shop-roles.asmer.fs.a-level.com.ua/${
+            images && images[0].url
+          }`}
+          alt="*it has to be pic of product*"
+          width="200"
+          height="200"
+        ></img>
+      </Link>
+    </li>
+  );
+};
+export default Good;

+ 10 - 0
src/components/Goods/Goods.module.css

@@ -0,0 +1,10 @@
+.goods_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;
+}

+ 53 - 0
src/components/Goods/Goods.tsx

@@ -0,0 +1,53 @@
+import { useEffect, useState } from 'react';
+import { Route } from 'react-router-dom';
+import { connect } from 'react-redux';
+import { useRouteMatch } from 'react-router-dom';
+
+import s from './Goods.module.css';
+import { asyncGetGoods } from '../../redux/goods/operation';
+import { IGoodsProps, IGoodsState } from '../../typescript/goods/interfaces';
+import { TRouteMatchName } from '../../typescript/goods/types';
+
+import Good from './Good/Good';
+import Pagination from '../PaginationBar/PaginationBar';
+
+const Goods = ({ goodsArr, getGoods }: IGoodsProps) => {
+  const {
+    params: { name: routName },
+  }: TRouteMatchName = useRouteMatch();
+  console.log(routName);
+  useEffect(() => {
+    getGoods();
+  }, [getGoods]);
+
+  const [page, setPage] = useState<number>(1);
+  const handlePageChange = (page: number) => setPage(page);
+
+  return goodsArr ? (
+    <Route>
+      <ul className={s.goods_list}>
+        {goodsArr.length !== 0 &&
+          goodsArr
+            .slice((page - 1) * 5, page * 5)
+            .map((good: any, i: number) => (
+              <Good key={i} good={good} routName={routName} />
+            ))}
+      </ul>
+      <Pagination
+        page={page}
+        total={Math.ceil(goodsArr.length / 5)}
+        handlePageChange={handlePageChange}
+      />
+    </Route>
+  ) : null;
+};
+
+const mapStateToProps = (state: IGoodsState) => ({
+  goodsArr: state.goods.goodsArr,
+});
+
+const mapDispatchToProps = (dispatch: any) => ({
+  getGoods: () => dispatch(asyncGetGoods()),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Goods);

+ 0 - 0
src/components/Just/Just.module.css


+ 0 - 9
src/components/Just/Just.tsx

@@ -1,9 +0,0 @@
-import { useState } from 'react';
-
-import s from './Just.module.css';
-
-const Just = () => {
-  return <>Just</>;
-};
-
-export default Just;

+ 50 - 0
src/components/Navigation/Button/Button.module.css

@@ -0,0 +1,50 @@
+.button_navigation {
+  background-color: #b3b3b3;
+  transition: all 250ms cubic-bezier(0.4, 0, 0.2, 1);
+  font-family: 'Mountains of Christmas', cursive;
+  text-align: center;
+  display: flex;
+  justify-content: center;
+  justify-items: center;
+  text-align: center;
+  cursor: pointer;
+  width: 80px;
+  font-size: 16px;
+  height: auto;
+  box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2),
+    0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12);
+}
+.button_navigation {
+  margin-right: 2%;
+}
+
+.navigation_link {
+  display: block;
+  width: 72px;
+  text-decoration: none;
+  color: rgb(255, 255, 255);
+}
+
+.navigation_link_active {
+  color: rgb(37, 156, 253);
+}
+
+@media (min-width: 767px) {
+  .button_navigation {
+    width: 100px;
+    font-size: 18px;
+  }
+  .navigation_link {
+    width: 90px;
+  }
+}
+
+@media (min-width: 1400px) {
+  .button_navigation {
+    width: 120px;
+    font-size: 20px;
+  }
+  .navigation_link {
+    width: 110px;
+  }
+}

+ 26 - 0
src/components/Navigation/Button/Button.tsx

@@ -0,0 +1,26 @@
+import React from 'react';
+import { NavLink } from 'react-router-dom';
+
+import s from './Button.module.css';
+
+type ButtonProps = {
+  text: string;
+  to: string;
+};
+
+const Button = ({ text, to }: ButtonProps) => {
+  return (
+    <button type="button" className={s.button_navigation}>
+      <NavLink
+        exact
+        to={to}
+        activeClassName={s.navigation_link_active}
+        className={s.navigation_link}
+      >
+        {text}
+      </NavLink>
+    </button>
+  );
+};
+
+export default Button;

+ 19 - 0
src/components/Navigation/Navigation.module.css

@@ -0,0 +1,19 @@
+.wrapper__button {
+  width: 100%;
+  position: fixed;
+  background-color: rgb(95, 92, 92);
+  width: 100vw;
+  z-index: 100;
+  display: flex;
+  justify-content: center;
+  padding: 10px 0;
+}
+
+.wrapper__button__logout {
+  width: 50%;
+  display: flex;
+  justify-content: flex-end;
+}
+
+@media (min-width: 767px) {
+}

+ 24 - 0
src/components/Navigation/Navigation.tsx

@@ -0,0 +1,24 @@
+import { useDispatch } from 'react-redux';
+
+import Button from './Button/Button';
+// import { asyncLogout } from '../../redux/authorization/operations';
+
+import s from './Navigation.module.css';
+
+const Navigation = () => {
+  // const dispatch = useDispatch();
+  // const handleLogout = async () => dispatch(asyncLogout())
+
+  return (
+    <div className={s.wrapper__button}>
+      <Button text="Categories" to="/categories" />
+      <Button text="Orders" to="/orders" />
+      {/* <div className={s.wrapper__button__logout}>
+        <div onClick={handleLogout}>
+          <Button  text="Logout" to="/authorization" />
+        </div>
+      </div> */}
+    </div>
+  );
+};
+export default Navigation;

+ 22 - 0
src/components/PaginationBar/PaginationBar.jsx

@@ -0,0 +1,22 @@
+import Pagination from 'react-js-pagination';
+import 'bootstrap/dist/css/bootstrap.min.css';
+
+import s from './PaginationBar.module.css';
+
+function PaginationBar({ page, total, handlePageChange }) {
+  return (
+    <div className={s.pagination_wrapper}>
+      <Pagination
+        itemClass="page-item"
+        linkClass="page-link"
+        activePage={page}
+        itemsCountPerPage={1}
+        totalItemsCount={total}
+        pageRangeDisplayed={total > 5 ? 5 : total}
+        onChange={handlePageChange}
+      />
+    </div>
+  );
+}
+
+export default PaginationBar;

+ 6 - 0
src/components/PaginationBar/PaginationBar.module.css

@@ -0,0 +1,6 @@
+.pagination_wrapper {
+  margin: 0 auto;
+  padding-bottom: 15px;
+  display: flex;
+  justify-content: center;
+}

+ 16 - 12
src/index.tsx

@@ -1,4 +1,5 @@
 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';
@@ -6,22 +7,25 @@ import { ToastContainer } from 'react-toastify';
 import 'modern-normalize/modern-normalize.css';
 import './index.css';
 import App from './App';
+import { store } from './redux/store';
 
 ReactDOM.render(
   <React.StrictMode>
     <Suspense fallback={null}>
-      <App />
-      <ToastContainer
-        position="top-right"
-        autoClose={3000}
-        hideProgressBar={false}
-        newestOnTop={false}
-        closeOnClick
-        rtl={false}
-        pauseOnFocusLoss
-        draggable
-        pauseOnHover
-      />
+      <Provider store={store}>
+        <App />
+        <ToastContainer
+          position="top-right"
+          autoClose={3000}
+          hideProgressBar={false}
+          newestOnTop={false}
+          closeOnClick
+          rtl={false}
+          pauseOnFocusLoss
+          draggable
+          pauseOnHover
+        />
+      </Provider>
     </Suspense>
   </React.StrictMode>,
   document.getElementById('root'),

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

@@ -0,0 +1,14 @@
+import { categoriesActionType } from '../actionType';
+import { ICategory } from '../../../typescript/categories/interfaces';
+
+const actionGetCategoriesSusses = (arr: ICategory[]) => ({
+  type: categoriesActionType.success,
+  payload: arr,
+});
+
+const actionGetCategoriesReject = () => ({
+  type: categoriesActionType.reject,
+  payload: {},
+});
+
+export { actionGetCategoriesSusses, actionGetCategoriesReject };

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

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

+ 17 - 0
src/redux/categories/operation/index.ts

@@ -0,0 +1,17 @@
+import {
+  actionGetCategoriesSusses,
+  actionGetCategoriesReject,
+} from '../action';
+import { categoriesGQL } from '../../../api-data';
+import { ICategory } from '../../../typescript/categories/interfaces';
+
+const asyncGetCategories = () => async (dispatch: any) => {
+  try {
+    const data = await categoriesGQL<ICategory[]>();
+    data && dispatch(actionGetCategoriesSusses(data));
+  } catch (e) {
+    dispatch(actionGetCategoriesReject());
+  }
+};
+
+export { asyncGetCategories };

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

@@ -0,0 +1,22 @@
+import { categoriesActionType } from '../actionType';
+import { ICategoriesReducer } from '../../../typescript/reduxTs/categories/interfaces';
+
+export const initialState = {
+  categoriesArr: [],
+};
+
+export function categoriesReducer(
+  state = initialState,
+  { type, payload }: ICategoriesReducer,
+) {
+  switch (type) {
+    case categoriesActionType.success:
+      return {
+        categoriesArr: payload,
+      };
+    case categoriesActionType.reject:
+      return state;
+    default:
+      return state;
+  }
+}

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

@@ -0,0 +1,14 @@
+import { goodsActionType } from '../actionType';
+import { IGood } from '../../../typescript/goods/interfaces';
+
+const actionGetGoodsSusses = (arr: IGood[]) => ({
+  type: goodsActionType.success,
+  payload: arr,
+});
+
+const actionGetGoodsReject = () => ({
+  type: goodsActionType.reject,
+  payload: {},
+});
+
+export { actionGetGoodsSusses, actionGetGoodsReject };

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

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

+ 14 - 0
src/redux/goods/operation/index.ts

@@ -0,0 +1,14 @@
+import { actionGetGoodsSusses, actionGetGoodsReject } from '../action';
+import { goodsGQL } from '../../../api-data';
+import { IGood } from '../../../typescript/goods/interfaces';
+
+const asyncGetGoods = () => async (dispatch: any) => {
+  try {
+    const data = await goodsGQL<IGood[]>();
+    data && dispatch(actionGetGoodsSusses(data));
+  } catch (e) {
+    dispatch(actionGetGoodsReject());
+  }
+};
+
+export { asyncGetGoods };

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

@@ -0,0 +1,22 @@
+import { goodsActionType } from '../actionType';
+import { IGoodsReducer } from '../../../typescript/reduxTs/goods/interfaces';
+
+const initialState = {
+  goodsArr: [],
+};
+
+export function goodsReducer(
+  state = initialState,
+  { type, payload }: IGoodsReducer,
+) {
+  switch (type) {
+    case goodsActionType.success:
+      return {
+        goodsArr: payload,
+      };
+    case goodsActionType.reject:
+      return state;
+    default:
+      return state;
+  }
+}

+ 8 - 0
src/redux/rootReducer/index.js

@@ -0,0 +1,8 @@
+import { combineReducers } from 'redux';
+import { categoriesReducer } from '../categories/reducer';
+import { goodsReducer } from '../goods/reducer';
+
+export const rootReducer = combineReducers({
+  categories: categoriesReducer,
+  goods: goodsReducer,
+});

+ 10 - 0
src/redux/store/index.js

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

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

@@ -0,0 +1,16 @@
+export interface ICategory {
+  _id: string | undefined;
+  name?: string | undefined;
+  createdAt: string | undefined;
+}
+
+export interface ICategoriesProps {
+  categoriesArr: ICategory[] | [];
+  getCategories: () => void;
+}
+
+export interface ICategoriesState {
+  categories: {
+    categoriesArr: ICategory[];
+  };
+}

+ 5 - 0
src/typescript/categories/types.ts

@@ -0,0 +1,5 @@
+export type TRouteMatch = {
+  params: {
+    id: string;
+  };
+};

+ 44 - 0
src/typescript/goods/interfaces.ts

@@ -0,0 +1,44 @@
+export interface IGood {
+  _id: string | undefined;
+  name?: string | undefined;
+  createdAt: string | undefined;
+  price?: number;
+  images?:
+    | [
+        {
+          url: string;
+        },
+      ]
+    | null;
+}
+
+export interface IGoodItem {
+  good: IGood;
+  routName: string;
+}
+
+export interface IGoodDetail {
+  _id: string | undefined;
+  name?: string | undefined;
+  createdAt: string | undefined;
+  price?: number;
+  images?:
+    | [
+        {
+          url: string;
+        },
+      ]
+    | null;
+  description: string | undefined;
+}
+
+export interface IGoodsProps {
+  goodsArr: IGood[] | [];
+  getGoods: () => void;
+}
+
+export interface IGoodsState {
+  goods: {
+    goodsArr: IGood[];
+  };
+}

+ 11 - 0
src/typescript/goods/types.ts

@@ -0,0 +1,11 @@
+export type TRouteMatchId = {
+  params: {
+    id: string;
+  };
+};
+
+export type TRouteMatchName = {
+  params: {
+    name: string;
+  };
+};

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

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

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

@@ -0,0 +1,7 @@
+import { ICategory } from '../../categories/interfaces';
+import { ETypes } from './enum';
+
+export interface ICategoriesReducer {
+  type: ETypes;
+  payload: ICategory[];
+}

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

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

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

@@ -0,0 +1,7 @@
+import { IGood } from '../../goods/interfaces';
+import { ETypes } from './enum';
+
+export interface IGoodsReducer {
+  type: ETypes;
+  payload: IGood[];
+}