#1 merge saga into main branch

Merged
ilya_shyian merged 6 commits from ilya_shyian/saga into ilya_shyian/main 2 years ago
97 changed files with 967 additions and 872 deletions
  1. 1 0
      package.json
  2. 2 2
      src/App.js
  3. 16 10
      src/actions/actionAboutMe.js
  4. 16 21
      src/actions/actionCatAll.js
  5. 19 8
      src/actions/actionCatByIdFull.js
  6. 0 8
      src/actions/actionCatByIdFullClear.js
  7. 14 8
      src/actions/actionCategoriesPage.js
  8. 0 6
      src/actions/actionCategoriesPageClear.js
  9. 16 7
      src/actions/actionCategoriesSearchPage.js
  10. 0 6
      src/actions/actionCategoriesSearchPageClear.js
  11. 0 7
      src/actions/actionCategoryDeleteFull.js
  12. 33 26
      src/actions/actionCategoryGoods.js
  13. 17 11
      src/actions/actionCategoryPage.js
  14. 0 9
      src/actions/actionCategoryPageClear.js
  15. 11 4
      src/actions/actionCategoryUpdate.js
  16. 6 9
      src/actions/actionCategoryUpsert.js
  17. 16 21
      src/actions/actionCatsFind.js
  18. 21 11
      src/actions/actionGoodPage.js
  19. 0 9
      src/actions/actionGoodPageClear.js
  20. 9 4
      src/actions/actionGoodUpdate.js
  21. 6 9
      src/actions/actionGoodUpsert.js
  22. 16 21
      src/actions/actionGoodsAll.js
  23. 31 25
      src/actions/actionGoodsFind.js
  24. 15 7
      src/actions/actionGoodsPage.js
  25. 0 7
      src/actions/actionGoodsPageClear.js
  26. 14 17
      src/actions/actionGoodsPopular.js
  27. 14 8
      src/actions/actionGoodsSearchPage.js
  28. 0 6
      src/actions/actionGoodsSearchPageClear.js
  29. 13 7
      src/actions/actionLogin.js
  30. 8 5
      src/actions/actionLogout.js
  31. 18 11
      src/actions/actionOrderPage.js
  32. 0 10
      src/actions/actionOrderPageClear.js
  33. 19 4
      src/actions/actionOrderUpdate.js
  34. 8 22
      src/actions/actionOrderUpsert.js
  35. 8 12
      src/actions/actionOrders.js
  36. 18 23
      src/actions/actionOrdersAll.js
  37. 16 21
      src/actions/actionOrdersFind.js
  38. 17 9
      src/actions/actionOrdersPage.js
  39. 0 6
      src/actions/actionOrdersPageClear.js
  40. 17 8
      src/actions/actionOrdersSearchPage.js
  41. 0 6
      src/actions/actionOrdersSearchPageClear.js
  42. 11 8
      src/actions/actionPageStart.js
  43. 9 0
      src/actions/actionPromisesClear.js
  44. 11 5
      src/actions/actionRegister.js
  45. 12 64
      src/actions/actionRootCats.js
  46. 12 7
      src/actions/actionUpdateAvatar.js
  47. 10 7
      src/actions/actionUploadFiles.js
  48. 18 10
      src/actions/actionUserPage.js
  49. 0 8
      src/actions/actionUserPageClear.js
  50. 12 7
      src/actions/actionUserUpdate.js
  51. 8 10
      src/actions/actionUserUpsert.js
  52. 16 21
      src/actions/actionUsersAll.js
  53. 19 24
      src/actions/actionUsersFind.js
  54. 13 7
      src/actions/actionUsersPage.js
  55. 0 6
      src/actions/actionUsersPageClear.js
  56. 13 7
      src/actions/actionUsersSearchPage.js
  57. 0 6
      src/actions/actionUsersSearchPageClear.js
  58. 6 5
      src/components/CartPage/CartItem.js
  59. 16 7
      src/components/CartPage/index.js
  60. 1 1
      src/components/DashboardPage/ProfileForm/index.js
  61. 1 1
      src/components/GoodPage/index.js
  62. 1 1
      src/components/GoodsPage/index.js
  63. 2 4
      src/components/LayoutPage/GoodsPageContainer.js
  64. 1 2
      src/components/LayoutPage/GoodsSearchPageContainer.js
  65. 15 13
      src/components/LayoutPage/index.js
  66. 9 6
      src/components/Root/index.js
  67. 4 8
      src/components/admin/AdminCategoryPage/CategoryForm.js
  68. 5 6
      src/components/admin/AdminGoodPage/GoodForm.js
  69. 12 23
      src/components/admin/AdminLayoutPage/AdminCategoryLayout/AdminCategoriesPageContainer.js
  70. 12 23
      src/components/admin/AdminLayoutPage/AdminCategoryLayout/AdminCategoriesSearchPageContainer.js
  71. 1 2
      src/components/admin/AdminLayoutPage/AdminCategoryLayout/AdminCategoryPageContainer.js
  72. 1 2
      src/components/admin/AdminLayoutPage/AdminGoodLayout/AdminGoodPageContainer.js
  73. 3 3
      src/components/admin/AdminLayoutPage/AdminGoodLayout/AdminGoodsPageContainer.js
  74. 1 2
      src/components/admin/AdminLayoutPage/AdminGoodLayout/AdminGoodsSearchPageContainer.js
  75. 1 2
      src/components/admin/AdminLayoutPage/AdminOrderLayout/AdminOrderPageContainer.js
  76. 2 3
      src/components/admin/AdminLayoutPage/AdminOrderLayout/AdminOrdersPageContainer.js
  77. 4 4
      src/components/admin/AdminLayoutPage/AdminOrderLayout/AdminOrdersSearchPageContainer.js
  78. 1 2
      src/components/admin/AdminLayoutPage/AdminUserLayout/AdminUserPageContainer.js
  79. 1 2
      src/components/admin/AdminLayoutPage/AdminUserLayout/AdminUsersPageContainer.js
  80. 1 2
      src/components/admin/AdminLayoutPage/AdminUserLayout/AdminUsersSearchPageContainer.js
  81. 4 8
      src/components/admin/AdminOrderPage/OrderForm.js
  82. 51 13
      src/components/admin/AdminUserPage.js/UserForm.js
  83. 0 1
      src/components/admin/AdminUsersPage/AdminUserList.js
  84. 1 1
      src/components/common/AuthModal/LoginForm.js
  85. 1 1
      src/components/common/AuthModal/RegisterForm.js
  86. 8 6
      src/components/common/DrawerCart/DrawerCart.js
  87. 3 15
      src/components/common/SearchBar/SearchBar.js
  88. 7 6
      src/components/layout/Header/LogoutIcon/index.js
  89. 5 6
      src/components/layout/Header/index.js
  90. 1 1
      src/helpers/aclList.js
  91. 1 1
      src/helpers/index.js
  92. 0 4
      src/images/defaultAvatarImage.png:Zone.Identifier
  93. 10 0
      src/reducers/authReducer.js
  94. 15 45
      src/reducers/feedReducer.js
  95. 21 4
      src/reducers/index.js
  96. 62 8
      src/reducers/promiseReducer.js
  97. 77 0
      yarn.lock

+ 1 - 0
package.json

@@ -27,6 +27,7 @@
     "react-sortable-tree": "^2.8.0",
     "redux": "^4.2.0",
     "redux-devtools-extension": "^2.13.9",
+    "redux-saga": "^1.1.3",
     "redux-thunk": "^2.4.1",
     "web-vitals": "^2.1.4",
     "yup": "^0.32.11"

+ 2 - 2
src/App.js

@@ -1,7 +1,7 @@
 import "./App.css";
 import { Box } from "@mui/material";
 import { BrowserRouter } from "react-router-dom";
-import { Root } from "./components/Root";
+import { CRoot, Root } from "./components/Root";
 import { Provider } from "react-redux";
 import { store } from "./reducers";
 import { UIContextProvider } from "./components/UIContext";
@@ -12,7 +12,7 @@ function App() {
             <BrowserRouter>
                 <UIContextProvider>
                     <Box className="App">
-                        <Root />
+                        <CRoot />
                     </Box>
                 </UIContextProvider>
             </BrowserRouter>

+ 16 - 10
src/actions/actionAboutMe.js

@@ -1,29 +1,35 @@
 import { gql } from "../helpers";
 import { actionPromise } from "../reducers";
+import { promiseWorker } from "../reducers/promiseReducer";
+import { call, select } from "redux-saga/effects";
 
-export const actionAboutMe = () => async (dispatch, getState) => {
+export const actionAboutMe = () => ({ type: "ABOUT_ME" });
+
+export function* aboutMeWorker() {
     const {
         auth: {
             payload: {
                 sub: { _id },
             },
         },
-    } = getState();
-    await dispatch(
+    } = yield select();
+
+    yield call(
+        promiseWorker,
         actionPromise(
             "aboutMe",
             gql(
                 `query AboutMe($q:String){
-                        UserFindOne(query:$q){
-                            _id username name nick avatar{
-                                _id url
-                            }
-                        }
-                    }`,
+                UserFindOne(query:$q){
+                    _id username name nick avatar{
+                        _id url
+                    }
+                }
+            }`,
                 {
                     q: JSON.stringify([{ _id }]),
                 }
             )
         )
     );
-};
+}

+ 16 - 21
src/actions/actionCatAll.js

@@ -1,14 +1,11 @@
 import { actionPromise } from "../reducers";
 import { gql } from "../helpers";
 
-export const actionCatAll =
-    ({ limit = 20, skip = 0, promiseName = "catAll", orderBy = "_id" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(
-            actionPromise(
-                promiseName,
-                gql(
-                    `query CatAll($query:String){
+export const actionCatAll = ({ limit = 20, skip = 0, promiseName = "catAll", orderBy = "_id" } = {}) =>
+    actionPromise(
+        promiseName,
+        gql(
+            `query CatAll($query:String){
                         CategoryFind(query: $query){
                             _id name
                             parent{
@@ -22,17 +19,15 @@ export const actionCatAll =
                             }
                         }
                     }`,
+            {
+                query: JSON.stringify([
+                    {},
                     {
-                        query: JSON.stringify([
-                            {},
-                            {
-                                limit: !!limit ? limit : 100,
-                                skip: skip,
-                                orderBy,
-                            },
-                        ]),
-                    }
-                )
-            )
-        );
-    };
+                        limit: !!limit ? limit : 100,
+                        skip: skip,
+                        orderBy,
+                    },
+                ]),
+            }
+        )
+    );

+ 19 - 8
src/actions/actionCatByIdFull.js

@@ -1,12 +1,23 @@
+import { call, put, take } from "redux-saga/effects";
 import { actionFeedClear, actionPromiseClear } from "../reducers";
 import { actionFeedCategoryGoods } from "../reducers/feedReducer";
+import { promiseWorker } from "../reducers/promiseReducer";
 import { actionCatById } from "./actionCatById";
+import { actionPromisesClear } from "./actionPromisesClear";
 
-export const actionCatByIdFull =
-    ({ _id, orderBy }) =>
-    async (dispatch, getState) => {
-        const category = await dispatch(actionCatById({ _id }));
-        dispatch(actionFeedClear());
-        dispatch(actionPromiseClear("feedCategoryGoods"));
-        dispatch(actionFeedCategoryGoods({ category, orderBy, skip: 0 }));
-    };
+export const actionCatByIdFull = ({ _id, orderBy }) => ({ type: "CAT_BY_ID_FULL", payload: { _id, orderBy } });
+
+export function* catByIdFullWorker(action) {
+    const { _id, orderBy = "createdAt" } = action.payload || {};
+
+    const category = yield call(promiseWorker, actionCatById({ _id }));
+
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedCategoryGoods"));
+    yield put(actionFeedCategoryGoods({ category, orderBy, skip: 0 }));
+
+    yield take("CAT_BY_ID_FULL_CLEAR");
+
+    yield put(actionPromisesClear(["catById", "feedCategoryGoods"]));
+    yield put(actionFeedClear());
+}

+ 0 - 8
src/actions/actionCatByIdFullClear.js

@@ -1,8 +0,0 @@
-import { actionFeedClear, actionPromiseClear } from "../reducers";
-import { actionCatById } from "./actionCatById";
-
-export const actionCatByIdFullClear = () => async (dispatch, getState) => {
-    dispatch(actionPromiseClear("catById"));
-    dispatch(actionFeedClear());
-    dispatch(actionPromiseClear("feedCategoryGoods"));
-};

+ 14 - 8
src/actions/actionCategoriesPage.js

@@ -1,10 +1,16 @@
+import { put, take } from "redux-saga/effects";
 import { actionFeedCats, actionFeedClear, actionPromiseClear } from "../reducers";
 
-export const actionCategoriesPage =
-    ({ orderBy = "_id" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(actionFeedClear());
-        dispatch(actionPromiseClear("feedCatAll"));
-        dispatch(actionPromiseClear("categoryUpsert"));
-        dispatch(actionFeedCats({ skip: 0, orderBy }));
-    };
+export const actionCategoriesPage = ({ orderBy }) => ({ type: "CATEGORIES_PAGE", payload: { orderBy } });
+
+export function* categoriesPageWorker(action) {
+    const { orderBy = "_id" } = action.payload || {};
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedCatAll"));
+    yield put(actionPromiseClear("categoryUpsert"));
+    yield put(actionFeedCats({ skip: 0, orderBy }));
+
+    yield take("CATEGORIES_PAGE_CLEAR");
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedCatAll"));
+}

+ 0 - 6
src/actions/actionCategoriesPageClear.js

@@ -1,6 +0,0 @@
-import { actionFeedClear, actionPromiseClear } from "../reducers";
-
-export const actionCategoriesPageClear = () => async (dispatch, getState) => {
-    dispatch(actionFeedClear());
-    dispatch(actionPromiseClear("feedCatAll"));
-};

+ 16 - 7
src/actions/actionCategoriesSearchPage.js

@@ -1,9 +1,18 @@
+import { put, take } from "redux-saga/effects";
 import { actionFeedCatsFind, actionFeedClear, actionPromiseClear } from "../reducers";
 
-export const actionCategoriesSearchPage =
-    ({ orderBy = "_id", text = "" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(actionFeedClear());
-        dispatch(actionPromiseClear("feedCatsFind"));
-        dispatch(actionFeedCatsFind({ text, orderBy, skip: 0 }));
-    };
+export const actionCategoriesSearchPage = ({ orderBy = "_id", text = "" } = {}) => ({
+    type: "CATEGORIES_SEARCH_PAGE",
+    payload: { orderBy, text },
+});
+
+export function* categoriesSearchPageWorker(action) {
+    const { orderBy = "_id", text = "" } = action.payload || {};
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedCatsFind"));
+    yield put(actionFeedCatsFind({ text, orderBy, skip: 0 }));
+
+    yield take("CATEGORIES_SEARCH_PAGE_CLEAR");
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedCatsFind"));
+}

+ 0 - 6
src/actions/actionCategoriesSearchPageClear.js

@@ -1,6 +0,0 @@
-import { actionFeedClear, actionPromiseClear } from "../reducers";
-
-export const actionCategoriesSearchPageClear = () => async (dispatch, getState) => {
-    dispatch(actionFeedClear());
-    dispatch(actionPromiseClear("feedCatsFind"));
-};

+ 0 - 7
src/actions/actionCategoryDeleteFull.js

@@ -1,7 +0,0 @@
-import { gql } from "../helpers";
-
-import { actionPromise } from "../reducers";
-
-export const actionCategoryDelete =
-    ({ category, promiseName = "categoryDelete" } = {}) =>
-    async (dispatch, getState) => {};

+ 33 - 26
src/actions/actionCategoryGoods.js

@@ -1,17 +1,24 @@
+import { call, cancelled, select } from "redux-saga/effects";
 import { gql } from "../helpers";
 import { actionPromise } from "../reducers";
+import { promiseWorker } from "../reducers/promiseReducer";
 
-export const actionCategoryGoods =
-    ({ limit = 20, skip = 0, promiseName = "categoryGoods", orderBy = "_id", category } = {}) =>
-    async (dispatch, getState) => {
-        if (!category) {
-            return;
-        }
-        dispatch(
-            actionPromise(
-                promiseName,
-                gql(
-                    `query GCategoryGoods($query:String){
+export const actionCategoryGoods = ({ limit = 20, skip = 0, promiseName = "categoryGoods", orderBy = "_id", category } = {}) => ({
+    type: "CATEGORY_GOODS",
+    payload: { limit, skip, promiseName, orderBy, category },
+});
+export function* categoryGoodsWorker(action) {
+    const { limit = 20, skip = 0, promiseName = "categoryGoods", orderBy = "_id", category } = action.payload || {};
+    if (!category) {
+        return;
+    }
+
+    yield call(
+        promiseWorker,
+        actionPromise(
+            promiseName,
+            gql(
+                `query CategoryGoods($query:String){
                         GoodFind(query: $query){
                             _id name price images{
                                 _id url
@@ -22,19 +29,19 @@ export const actionCategoryGoods =
                             amount
                         }
                     }`,
-                    {
-                        query: JSON.stringify([
-                            {
-                                categories__in: [category._id],
-                            },
-                            {
-                                limit: !!limit ? limit : 100,
-                                skip: skip,
-                                orderBy,
-                            },
-                        ]),
-                    }
-                )
+                {
+                    query: JSON.stringify([
+                        {
+                            categories__in: [category._id],
+                        },
+                        {
+                            limit: !!limit ? limit : 100,
+                            skip: skip,
+                            orderBy,
+                        },
+                    ]),
+                }
             )
-        );
-    };
+        )
+    );
+}

+ 17 - 11
src/actions/actionCategoryPage.js

@@ -1,17 +1,23 @@
+import { put, take } from "redux-saga/effects";
 import { actionPromiseClear } from "../reducers";
 import { actionCatAll } from "./actionCatAll";
 import { actionCatById } from "./actionCatById";
 import { actionGoodsAll } from "./actionGoodsAll";
+import { actionPromisesClear } from "./actionPromisesClear";
 
-export const actionCategoryPage =
-    ({ _id, promiseName = "catById" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(actionGoodsAll());
-        dispatch(actionCatAll());
+export const actionCategoryPage = ({ _id, promiseName = "catById" } = {}) => ({ type: "CATEGORY_PAGE", payload: { _id, promiseName } });
+export function* categoryPageWorker(action) {
+    const { _id, promiseName = "catById" } = action.payload || {};
+    yield put(actionGoodsAll());
+    yield put(actionCatAll());
 
-        if (_id) {
-            dispatch(actionCatById({ _id, promiseName }));
-        } else {
-            dispatch(actionPromiseClear(promiseName));
-        }
-    };
+    if (_id) {
+        yield put(actionCatById({ _id, promiseName }));
+    } else {
+        yield put(actionPromiseClear(promiseName));
+    }
+
+    yield take("CATEGORY_PAGE_CLEAR");
+
+    yield put(actionPromisesClear([promiseName, "catAll", "goodsAll"]));
+}

+ 0 - 9
src/actions/actionCategoryPageClear.js

@@ -1,9 +0,0 @@
-import { actionPromiseClear } from "../reducers";
-
-export const actionCategoryPageClear =
-    ({ promiseName = "catById" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(actionPromiseClear("goodsAll"));
-        dispatch(actionPromiseClear(promiseName));
-        dispatch(actionPromiseClear("catAll"));
-    };

+ 11 - 4
src/actions/actionCategoryUpdate.js

@@ -1,7 +1,14 @@
+import { call, put } from "redux-saga/effects";
+import { promiseWorker } from "../reducers/promiseReducer";
 import { actionCatAll } from "./actionCatAll";
 import { actionCategoryUpsert } from "./actionCategoryUpsert";
+import { actionRootCats } from "./actionRootCats";
 
-export const actionCategoryUpdate = (category) => async (dispatch, getState) => {
-    await dispatch(actionCategoryUpsert(category));
-    await dispatch(actionCatAll());
-};
+export const actionCategoryUpdate = (category) => ({ type: "CATEGORY_UPDATE", payload: category });
+
+export function* categoryUpdateWorker(action) {
+    const category = action.payload || {};
+    yield call(promiseWorker, actionCategoryUpsert(category));
+    yield put(actionCatAll());
+    yield put(actionRootCats());
+}

+ 6 - 9
src/actions/actionCategoryUpsert.js

@@ -1,12 +1,11 @@
 import { gql } from "../helpers";
 import { actionPromise } from "../reducers";
 
-export const actionCategoryUpsert = (category) => async (dispatch) => {
-    dispatch(
-        actionPromise(
-            "categoryUpsert",
-            gql(
-                `mutation CatUpsert($category:CategoryInput!){
+export const actionCategoryUpsert = (category) =>
+    actionPromise(
+        "categoryUpsert",
+        gql(
+            `mutation CatUpsert($category:CategoryInput!){
                     CategoryUpsert(category:$category){
                         _id name
                         parent{
@@ -20,8 +19,6 @@ export const actionCategoryUpsert = (category) => async (dispatch) => {
                         }
                     }
                 }`,
-                { category }
-            )
+            { category }
         )
     );
-};

+ 16 - 21
src/actions/actionCatsFind.js

@@ -1,14 +1,11 @@
 import { gql } from "../helpers";
 import { actionPromise } from "../reducers";
 
-export const actionCatsFind =
-    ({ text = "", limit = 7, skip = 0, promiseName = "catsFind", orderBy = "_id" }) =>
-    async (dispatch, getState) => {
-        dispatch(
-            actionPromise(
-                promiseName,
-                gql(
-                    `query CatsFind($query:String){
+export const actionCatsFind = ({ text = "", limit = 7, skip = 0, promiseName = "catsFind", orderBy = "_id" }) =>
+    actionPromise(
+        promiseName,
+        gql(
+            `query CatsFind($query:String){
                         CategoryFind(query: $query){
                             _id name 
                             parent{
@@ -17,17 +14,15 @@ export const actionCatsFind =
 
                         }
                     }`,
+            {
+                query: JSON.stringify([
+                    { name__contains: text, _id__contains: text },
                     {
-                        query: JSON.stringify([
-                            { name__contains: text, _id__contains: text },
-                            {
-                                limit: !!limit ? limit : 5,
-                                skip,
-                                orderBy,
-                            },
-                        ]),
-                    }
-                )
-            )
-        );
-    };
+                        limit: !!limit ? limit : 5,
+                        skip,
+                        orderBy,
+                    },
+                ]),
+            }
+        )
+    );

+ 21 - 11
src/actions/actionGoodPage.js

@@ -1,15 +1,25 @@
+import { put, take } from "redux-saga/effects";
 import { actionPromiseClear } from "../reducers";
 import { actionCatAll } from "./actionCatAll";
 import { actionGoodById } from "./actionGoodById";
+import { actionPromisesClear } from "./actionPromisesClear";
 
-export const actionGoodPage =
-    ({ _id, promiseName = "goodById" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(actionCatAll());
-
-        if (_id) {
-            dispatch(actionGoodById({ _id, promiseName }));
-        } else {
-            dispatch(actionPromiseClear(promiseName));
-        }
-    };
+export const actionGoodPage = ({ _id, promiseName } = {}) => ({
+    type: "GOOD_PAGE",
+    payload: { _id, promiseName },
+});
+
+export function* goodPageWorker(action) {
+    const { _id, promiseName = "goodById" } = action.payload;
+    yield put(actionCatAll());
+
+    if (_id) {
+        yield put(actionGoodById({ _id, promiseName }));
+    } else {
+        yield put(actionPromiseClear(promiseName));
+    }
+
+    yield take("GOOD_PAGE_CLEAR");
+
+    yield put(actionPromisesClear([promiseName, "goodUpsert", "goodsAll"]));
+}

+ 0 - 9
src/actions/actionGoodPageClear.js

@@ -1,9 +0,0 @@
-import { actionPromiseClear } from "../reducers";
-
-export const actionGoodPageClear =
-    ({ promiseName = "goodById" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(actionPromiseClear(promiseName));
-        dispatch(actionPromiseClear("goodsAll"));
-        dispatch(actionPromiseClear("goodUpsert"));
-    };

+ 9 - 4
src/actions/actionGoodUpdate.js

@@ -1,7 +1,12 @@
+import { call, put } from "redux-saga/effects";
+import { promiseWorker } from "../reducers/promiseReducer";
 import { actionGoodsAll } from "./actionGoodsAll";
 import { actionGoodUpsert } from "./actionGoodUpsert";
 
-export const actionGoodUpdate = (good) => async (dispatch, getState) => {
-    await dispatch(actionGoodUpsert(good));
-    await dispatch(actionGoodsAll());
-};
+export const actionGoodUpdate = (good) => ({ type: "GOOD_UPDATE", payload: good });
+
+export function* goodUpdateWorker(action) {
+    const good = action.payload || {};
+    yield call(promiseWorker, actionGoodUpsert(good));
+    yield put(actionGoodsAll());
+}

+ 6 - 9
src/actions/actionGoodUpsert.js

@@ -1,18 +1,15 @@
 import { gql } from "../helpers";
 import { actionPromise } from "../reducers";
 
-export const actionGoodUpsert = (good) => async (dispatch) => {
-    dispatch(
-        actionPromise(
-            "goodUpsert",
-            gql(
-                `mutation GoodUpsert($good:GoodInput!){
+export const actionGoodUpsert = (good) =>
+    actionPromise(
+        "goodUpsert",
+        gql(
+            `mutation GoodUpsert($good:GoodInput!){
                     GoodUpsert(good:$good){
                         _id name
                     }
                   }`,
-                { good }
-            )
+            { good }
         )
     );
-};

+ 16 - 21
src/actions/actionGoodsAll.js

@@ -1,14 +1,11 @@
 import { gql } from "../helpers";
 import { actionPromise } from "../reducers";
 
-export const actionGoodsAll =
-    ({ limit = 20, skip = 0, promiseName = "goodsAll", orderBy = "_id" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(
-            actionPromise(
-                promiseName,
-                gql(
-                    `query GoodsAll($query:String){
+export const actionGoodsAll = ({ limit = 20, skip = 0, promiseName = "goodsAll", orderBy = "_id" } = {}) =>
+    actionPromise(
+        promiseName,
+        gql(
+            `query GoodsAll($query:String){
                         GoodFind(query: $query){
                             _id name price images{
                                 _id url
@@ -19,17 +16,15 @@ export const actionGoodsAll =
                             amount
                         }
                     }`,
+            {
+                query: JSON.stringify([
+                    {},
                     {
-                        query: JSON.stringify([
-                            {},
-                            {
-                                limit: !!limit ? limit : 100,
-                                skip: skip,
-                                orderBy,
-                            },
-                        ]),
-                    }
-                )
-            )
-        );
-    };
+                        limit: !!limit ? limit : 100,
+                        skip: skip,
+                        orderBy,
+                    },
+                ]),
+            }
+        )
+    );

+ 31 - 25
src/actions/actionGoodsFind.js

@@ -1,14 +1,20 @@
+import { call, delay, put } from "redux-saga/effects";
 import { gql } from "../helpers";
 import { actionPromise } from "../reducers";
-
-export const actionGoodsFind =
-    ({ text = "", limit = 7, skip = 0, promiseName = "goodsFind", orderBy = "_id" }) =>
-    async (dispatch, getState) => {
-        dispatch(
-            actionPromise(
-                promiseName,
-                gql(
-                    `query GoodsFind($query:String){
+import { promiseWorker } from "../reducers/promiseReducer";
+export const actionGoodsFind = ({ text, limit, skip, promiseName, orderBy, delay }) => ({
+    type: "GOODS_FIND",
+    payload: { text, limit, skip, promiseName, orderBy, delay },
+});
+export function* goodsFindWorker(action) {
+    const { text = "", limit = 7, skip = 0, promiseName = "goodsFind", orderBy = "_id", timeout = 0 } = action.payload || {};
+    yield delay(timeout);
+    yield call(
+        promiseWorker,
+        actionPromise(
+            promiseName,
+            gql(
+                `query GoodsFind($query:String){
                         GoodFind(query: $query){
                             _id name price images{
                                 _id url
@@ -19,20 +25,20 @@ export const actionGoodsFind =
                             amount
                         }
                     }`,
-                    {
-                        query: JSON.stringify([
-                            {
-                                name__contains: text,
-                                description__contains: text,
-                            },
-                            {
-                                limit: !!limit ? limit : 5,
-                                skip,
-                                orderBy,
-                            },
-                        ]),
-                    }
-                )
+                {
+                    query: JSON.stringify([
+                        {
+                            name__contains: text,
+                            description__contains: text,
+                        },
+                        {
+                            limit: !!limit ? limit : 5,
+                            skip,
+                            orderBy,
+                        },
+                    ]),
+                }
             )
-        );
-    };
+        )
+    );
+}

+ 15 - 7
src/actions/actionGoodsPage.js

@@ -1,9 +1,17 @@
+import { put, take } from "redux-saga/effects";
 import { actionFeedClear, actionFeedGoods, actionPromiseClear } from "../reducers";
+import { actionPromisesClear } from "./actionPromisesClear";
 
-export const actionGoodsPage =
-    ({ orderBy }) =>
-    async (dispatch, getState) => {
-        dispatch(actionFeedClear());
-        dispatch(actionPromiseClear("feedGoodsAll"));
-        dispatch(actionFeedGoods({ skip: 0, orderBy }));
-    };
+export const actionGoodsPage = ({ orderBy }) => ({ type: "GOODS_PAGE", payload: { orderBy } });
+export function* goodsPageWorker(action) {
+    const { orderBy = "_id" } = action.payload || {};
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedGoodsAll"));
+    yield put(actionFeedGoods({ skip: 0, orderBy }));
+
+    yield take("GOODS_PAGE_CLEAR");
+
+    yield put(actionFeedClear());
+
+    yield put(actionPromisesClear(["goodUpsert", "feedGoodsAll"]));
+}

+ 0 - 7
src/actions/actionGoodsPageClear.js

@@ -1,7 +0,0 @@
-import { actionFeedClear, actionPromiseClear } from "../reducers";
-
-export const actionGoodsPageClear = () => async (dispatch, getState) => {
-    dispatch(actionFeedClear());
-    dispatch(actionPromiseClear("feedGoodsAll"));
-    dispatch(actionPromiseClear("goodUpsert"));
-};

+ 14 - 17
src/actions/actionGoodsPopular.js

@@ -2,12 +2,11 @@ import { gql } from "../helpers";
 
 import { actionPromise } from "../reducers";
 
-export const actionGoodsPopular = () => async (dispatch, getState) => {
-    dispatch(
-        actionPromise(
-            "goodsPopular",
-            gql(
-                `query GoodsPopular($query:String){
+export const actionGoodsPopular = () =>
+    actionPromise(
+        "goodsPopular",
+        gql(
+            `query GoodsPopular($query:String){
                     GoodFind(query: $query){
                         _id name price amount
                         images{
@@ -15,16 +14,14 @@ export const actionGoodsPopular = () => async (dispatch, getState) => {
                         }
                     }
                 }`,
-                {
-                    query: JSON.stringify([
-                        {},
-                        {
-                            limit: 15,
-                            orderBy: "popular",
-                        },
-                    ]),
-                }
-            )
+            {
+                query: JSON.stringify([
+                    {},
+                    {
+                        limit: 15,
+                        orderBy: "popular",
+                    },
+                ]),
+            }
         )
     );
-};

+ 14 - 8
src/actions/actionGoodsSearchPage.js

@@ -1,9 +1,15 @@
-import { actionFeedCats, actionFeedCatsFind, actionFeedClear, actionFeedGoodsFind, actionPromiseClear } from "../reducers";
+import { put, take } from "redux-saga/effects";
+import { actionFeedClear, actionFeedGoodsFind, actionPromiseClear } from "../reducers";
 
-export const actionGoodsSearchPage =
-    ({ orderBy = "_id", text }) =>
-    async (dispatch, getState) => {
-        dispatch(actionFeedClear());
-        dispatch(actionPromiseClear("feedGoodsFind"));
-        dispatch(actionFeedGoodsFind({ text, orderBy, skip: 0 }));
-    };
+export const actionGoodsSearchPage = ({ orderBy = "_id", text } = {}) => ({ type: "GOODS_SEARCH_PAGE", payload: { orderBy, text } });
+
+export function* goodsSearchPageWorker(action) {
+    const { orderBy = "_id", text } = action.payload || {};
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedGoodsFind"));
+    yield put(actionFeedGoodsFind({ text, orderBy, skip: 0 }));
+    yield take("GOODS_SEARCH_PAGE_CLEAR");
+
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedGoodsFind"));
+}

+ 0 - 6
src/actions/actionGoodsSearchPageClear.js

@@ -1,6 +0,0 @@
-import { actionFeedClear, actionPromiseClear } from "../reducers";
-
-export const actionGoodsSearchPageClear = () => async (dispatch, getState) => {
-    dispatch(actionFeedClear());
-    dispatch(actionPromiseClear("feedGoodsFind"));
-};

+ 13 - 7
src/actions/actionLogin.js

@@ -3,10 +3,15 @@ import { gql } from "../helpers";
 import { actionAuthLogin } from "../reducers";
 import { actionAboutMe } from "./actionAboutMe";
 import { actionLogout } from "./actionLogout";
+import { promiseWorker } from "../reducers/promiseReducer";
+import { call, put } from "redux-saga/effects";
 
-export const actionLogin = (username, password) => async (dispatch, getState) => {
-    await dispatch(actionLogout());
-    const token = await dispatch(
+export const actionLogin = (username, password) => ({ type: "LOGIN", payload: { username, password } });
+export function* loginWorker(action) {
+    const { username, password } = action.payload || {};
+    yield call(promiseWorker, actionLogout());
+    const token = yield call(
+        promiseWorker,
         actionPromise(
             "login",
             gql(
@@ -19,11 +24,12 @@ export const actionLogin = (username, password) => async (dispatch, getState) =>
             )
         )
     );
+
     if (typeof token === "string") {
-        await dispatch(actionAuthLogin(token));
+        yield put(actionAuthLogin(token));
     } else {
-        await dispatch(actionAuthLogin(token.token));
+        yield put(actionAuthLogin(token.token));
     }
 
-    await dispatch(actionAboutMe());
-};
+    yield put(actionAboutMe());
+}

+ 8 - 5
src/actions/actionLogout.js

@@ -1,8 +1,11 @@
+import { put } from "redux-saga/effects";
 import { actionCartClear, actionPromiseClear } from "../reducers";
 import { actionAuthLogout } from "../reducers";
 
-export const actionLogout = () => async (dispatch) => {
-    dispatch(actionCartClear());
-    dispatch(actionAuthLogout());
-    dispatch(actionPromiseClear("aboutMe"));
-};
+export const actionLogout = () => ({ type: "LOGOUT" });
+
+export function* logoutWorker() {
+    yield put(actionCartClear());
+    yield put(actionAuthLogout());
+    yield put(actionPromiseClear("aboutMe"));
+}

+ 18 - 11
src/actions/actionOrderPage.js

@@ -1,17 +1,24 @@
+import { put, take } from "redux-saga/effects";
 import { actionPromiseClear } from "../reducers";
 import { actionGoodsAll } from "./actionGoodsAll";
 import { actionOrderById } from "./actionOrderById";
+import { actionPromisesClear } from "./actionPromisesClear";
 import { actionUsersAll } from "./actionUsersAll";
 
-export const actionOrderPage =
-    ({ _id, promiseName = "orderById" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(actionUsersAll());
-        dispatch(actionGoodsAll());
+export const actionOrderPage = ({ _id, promiseName = "orderById" } = {}) => ({ type: "ORDER_PAGE", payload: { _id, promiseName } });
 
-        if (_id) {
-            dispatch(actionOrderById({ _id, promiseName }));
-        } else {
-            dispatch(actionPromiseClear(promiseName));
-        }
-    };
+export function* orderPageWorker(action) {
+    const { _id, promiseName = "orderById" } = action.payload || {};
+    yield put(actionUsersAll());
+    yield put(actionGoodsAll());
+
+    if (_id) {
+        yield put(actionOrderById({ _id, promiseName }));
+    } else {
+        yield put(actionPromiseClear(promiseName));
+    }
+
+    yield take("ORDER_PAGE_CLEAR");
+
+    yield put(actionPromisesClear(["orderUpsert", "goodsAll", promiseName, "usersAll"]));
+}

+ 0 - 10
src/actions/actionOrderPageClear.js

@@ -1,10 +0,0 @@
-import { actionPromiseClear } from "../reducers";
-
-export const actionOrderPageClear =
-    ({ promiseName = "orderById" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(actionPromiseClear("usersAll"));
-        dispatch(actionPromiseClear("goodsAll"));
-        dispatch(actionPromiseClear(promiseName));
-        dispatch(actionPromiseClear("orderUpsert"));
-    };

+ 19 - 4
src/actions/actionOrderUpdate.js

@@ -1,7 +1,22 @@
+import { call, put, select } from "redux-saga/effects";
+import { actionCartClear } from "../reducers";
+import { promiseWorker } from "../reducers/promiseReducer";
 import { actionOrdersAll } from "./actionOrdersAll";
 import { actionOrderUpsert } from "./actionOrderUpsert";
+export const actionOrderUpdate = (order) => ({ type: "ORDER_UPDATE", payload: order });
+export function* orderUpdateWorker(action) {
+    const order = action.payload || {};
+    if (!order?.orderGoods?.length) {
+        return;
+    }
+    yield call(promiseWorker, actionOrderUpsert(order));
+    yield put(actionOrdersAll());
 
-export const actionOrderUpdate = (order) => async (dispatch, getState) => {
-    await dispatch(actionOrderUpsert(order));
-    await dispatch(actionOrdersAll());
-};
+    const {
+        promise: { orderUpsert },
+    } = yield select();
+
+    if (orderUpsert.status === "FULFILLED") {
+        yield put(actionCartClear());
+    }
+}

+ 8 - 22
src/actions/actionOrderUpsert.js

@@ -1,32 +1,18 @@
 import { gql } from "../helpers";
 import { actionCartClear, actionPromise } from "../reducers";
 
-export const actionOrderUpsert = (order) => async (dispatch, getState) => {
-    if (!order?.orderGoods?.length) {
-        return;
-    }
-    await dispatch(
-        actionPromise(
-            "orderUpsert",
-            gql(
-                `mutation newOrder($order:OrderInput!){
+export const actionOrderUpsert = (order) =>
+    actionPromise(
+        "orderUpsert",
+        gql(
+            `mutation newOrder($order:OrderInput!){
                   OrderUpsert(order:$order){
                     _id price
                   }
                 }
             `,
-                {
-                    order,
-                }
-            )
+            {
+                order,
+            }
         )
     );
-    let {
-        promise: { orderUpsert },
-    } = getState();
-
-    if (orderUpsert.status === "FULFILLED") {
-        dispatch(actionCartClear());
-        // dispatch(actionOrders(token));
-    }
-};

+ 8 - 12
src/actions/actionOrders.js

@@ -1,13 +1,10 @@
 import { actionPromise } from "../reducers";
 import { gql } from "../helpers";
-export const actionOrders =
-    ({ promiseName = "orders" } = {}) =>
-    (dispatch) =>
-        dispatch(
-            actionPromise(
-                promiseName,
-                gql(
-                    `
+export const actionOrders = ({ promiseName = "orders" } = {}) =>
+    actionPromise(
+        promiseName,
+        gql(
+            `
             query orders($query:String){
                 OrderFind(query:$query){
                     _id price createdAt status 
@@ -21,7 +18,6 @@ export const actionOrders =
                 }
             }
           `,
-                    { query: JSON.stringify([{}, { orderBy: "-createdAt", limit: 200 }]) }
-                )
-            )
-        );
+            { query: JSON.stringify([{}, { orderBy: "-createdAt", limit: 200 }]) }
+        )
+    );

+ 18 - 23
src/actions/actionOrdersAll.js

@@ -1,14 +1,11 @@
 import { actionPromise } from "../reducers";
 import { gql } from "../helpers";
 
-export const actionOrdersAll =
-    ({ limit = 0, skip = 0, promiseName = "adminOrdersAll", orderBy = "_id", status = 0 } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(
-            actionPromise(
-                promiseName,
-                gql(
-                    `query OrdersAll($query:String){
+export const actionOrdersAll = ({ limit = 0, skip = 0, promiseName = "adminOrdersAll", orderBy = "_id", status = 0 } = {}) =>
+    actionPromise(
+        promiseName,
+        gql(
+            `query OrdersAll($query:String){
                         OrderFind(query: $query){
                             _id status price 
                             owner{
@@ -21,19 +18,17 @@ export const actionOrdersAll =
                             }
                         }
                     }`,
+            {
+                query: JSON.stringify([
                     {
-                        query: JSON.stringify([
-                            {
-                                status,
-                            },
-                            {
-                                limit: !!limit ? limit : 100,
-                                skip: skip,
-                                orderBy,
-                            },
-                        ]),
-                    }
-                )
-            )
-        );
-    };
+                        status,
+                    },
+                    {
+                        limit: !!limit ? limit : 100,
+                        skip: skip,
+                        orderBy,
+                    },
+                ]),
+            }
+        )
+    );

+ 16 - 21
src/actions/actionOrdersFind.js

@@ -1,14 +1,11 @@
 import { gql } from "../helpers";
 import { actionPromise } from "../reducers";
 
-export const actionOrdersFind =
-    ({ text = "", limit = 7, skip = 0, promiseName = "ordersFind", orderBy = "_id", status = "0" }) =>
-    async (dispatch, getState) => {
-        dispatch(
-            actionPromise(
-                promiseName,
-                gql(
-                    `query OrdersFind($query:String){
+export const actionOrdersFind = ({ text = "", limit = 7, skip = 0, promiseName = "ordersFind", orderBy = "_id", status = "0" }) =>
+    actionPromise(
+        promiseName,
+        gql(
+            `query OrdersFind($query:String){
                         OrderFind(query: $query){
                             _id status price 
                             owner{
@@ -16,17 +13,15 @@ export const actionOrdersFind =
                             }
                         }
                     }`,
+            {
+                query: JSON.stringify([
+                    { owner__username__contains: text, _id__contains: text, status__contains: text, status },
                     {
-                        query: JSON.stringify([
-                            { owner__username__contains: text, _id__contains: text, status__contains: text, status },
-                            {
-                                limit: !!limit ? limit : 5,
-                                skip,
-                                orderBy,
-                            },
-                        ]),
-                    }
-                )
-            )
-        );
-    };
+                        limit: !!limit ? limit : 5,
+                        skip,
+                        orderBy,
+                    },
+                ]),
+            }
+        )
+    );

+ 17 - 9
src/actions/actionOrdersPage.js

@@ -1,9 +1,17 @@
-import { actionFeedCats, actionFeedClear, actionFeedOrders, actionPromiseClear } from "../reducers";
-
-export const actionOrdersPage =
-    ({ orderBy = "_id", status = "0" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(actionFeedClear());
-        dispatch(actionPromiseClear("feedOrdersAll"));
-        dispatch(actionFeedOrders({ skip: 0, orderBy, status }));
-    };
+import { put, take } from "redux-saga/effects";
+import { actionFeedClear, actionFeedOrders, actionPromiseClear } from "../reducers";
+
+export const actionOrdersPage = ({ orderBy = "_id", status = "0" } = {}) => ({ type: "ORDERS_PAGE", payload: { orderBy, status } });
+
+export function* ordersPageWorker(action) {
+    const { orderBy = "_id", status = "0" } = action.payload || {};
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedOrdersAll"));
+
+    yield put(actionFeedOrders({ skip: 0, orderBy, status }));
+
+    yield take("ORDERS_PAGE_CLEAR");
+
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedOrdersAll"));
+}

+ 0 - 6
src/actions/actionOrdersPageClear.js

@@ -1,6 +0,0 @@
-import { actionFeedClear, actionPromiseClear } from "../reducers";
-
-export const actionOrdersPageClear = () => async (dispatch, getState) => {
-    dispatch(actionFeedClear());
-    dispatch(actionPromiseClear("feedOrdersAll"));
-};

+ 17 - 8
src/actions/actionOrdersSearchPage.js

@@ -1,9 +1,18 @@
-import { actionFeedCats, actionFeedCatsFind, actionFeedClear, actionFeedOrdersFind, actionPromiseClear } from "../reducers";
+import { put, take } from "redux-saga/effects";
+import { actionFeedClear, actionFeedOrdersFind, actionPromiseClear } from "../reducers";
 
-export const actionOrdersSearchPage =
-    ({ orderBy = "_id", text, status } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(actionFeedClear());
-        dispatch(actionPromiseClear("feedOrdersFind"));
-        dispatch(actionFeedOrdersFind({ text, orderBy, skip: 0, status }));
-    };
+export const actionOrdersSearchPage = ({ orderBy = "_id", text, status } = {}) => ({
+    type: "ORDERS_SEARCH_PAGE",
+    payload: { orderBy, text, status },
+});
+export function* ordersSearchPageWorker(action) {
+    const { orderBy = "_id", text, status } = action.payload || {};
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedOrdersFind"));
+    yield put(actionFeedOrdersFind({ text, orderBy, skip: 0, status }));
+
+    yield take("ORDERS_SEARCH_PAGE_CLEAR");
+
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedOrdersFind"));
+}

+ 0 - 6
src/actions/actionOrdersSearchPageClear.js

@@ -1,6 +0,0 @@
-import { actionFeedClear, actionPromiseClear } from "../reducers";
-
-export const actionOrdersSearchPageClear = () => async (dispatch, getState) => {
-    dispatch(actionFeedClear());
-    dispatch(actionPromiseClear("feedOrdersFind"));
-};

+ 11 - 8
src/actions/actionPageStart.js

@@ -3,18 +3,21 @@ import { actionCatAll } from "./actionCatAll";
 import { actionGoodsPopular } from "./actionGoodsPopular";
 import { actionOrders } from "./actionOrders";
 import { actionRootCats } from "./actionRootCats";
+import { put, select } from "redux-saga/effects";
 
-export const actionPageStart = () => async (dispatch, getState) => {
-    dispatch(actionRootCats());
-    dispatch(actionCatAll());
-    dispatch(actionGoodsPopular());
+export const actionPageStart = () => ({ type: "PAGE_START" });
+
+export function* pageStartWorker() {
+    yield put(actionRootCats());
+    yield put(actionCatAll());
+    yield put(actionGoodsPopular());
 
     const {
         auth: { token },
-    } = getState();
+    } = yield select();
 
     if (token) {
-        dispatch(actionAboutMe());
-        dispatch(actionOrders());
+        yield put(actionAboutMe());
+        yield put(actionOrders());
     }
-};
+}

+ 9 - 0
src/actions/actionPromisesClear.js

@@ -0,0 +1,9 @@
+import { all, put } from "redux-saga/effects";
+import { actionPromiseClear } from "../reducers";
+
+export const actionPromisesClear = (promises = []) => ({ type: "PROMISES_CLEAR", payload: promises });
+
+export function* promisesClearWorker(action) {
+    const promises = action.payload || [];
+    yield all(promises.map((promise) => put(actionPromiseClear(promise))));
+}

+ 11 - 5
src/actions/actionRegister.js

@@ -1,9 +1,15 @@
+import { call, put, select } from "redux-saga/effects";
 import { gql } from "../helpers";
 import { actionPromise } from "../reducers";
+import { promiseWorker } from "../reducers/promiseReducer";
 import { actionLogin } from "./actionLogin";
 
-export const actionRegister = (username, password) => async (dispatch, getState) => {
-    await dispatch(
+export const actionRegister = (username, password) => ({ type: "REGISTER", payload: { username, password } });
+
+export function* registerWorker(action) {
+    const { username, password } = action.payload || {};
+    yield call(
+        promiseWorker,
         actionPromise(
             "register",
             gql(
@@ -21,8 +27,8 @@ export const actionRegister = (username, password) => async (dispatch, getState)
     );
     const {
         promise: { register },
-    } = getState();
+    } = yield select();
     if (register.status === "FULFILLED") {
-        dispatch(actionLogin(username, password));
+        yield put(actionLogin(username, password));
     }
-};
+}

+ 12 - 64
src/actions/actionRootCats.js

@@ -2,73 +2,21 @@ import { gql } from "../helpers";
 
 import { actionPromise } from "../reducers";
 
-export const actionRootCats = () => async (dispatch, getState) => {
-    dispatch(
-        actionPromise(
-            "rootCats",
-            gql(
-                `query rootCats($query:String) {
+export const actionRootCats = () =>
+    actionPromise(
+        "rootCats",
+        gql(
+            `query rootCats($query:String) {
                 CategoryFind(query: $query){
                     _id name
                 }
             }`,
-                {
-                    query: JSON.stringify([
-                        {
-                            parent: null,
-                        },
-                    ]),
-                }
-            )
+            {
+                query: JSON.stringify([
+                    {
+                        parent: null,
+                    },
+                ]),
+            }
         )
     );
-};
-
-// () => ({
-//     data: [
-//         {
-//             id: 1,
-//             name: 'Categoty 1',
-//         },
-//         {
-//             id: 2,
-//             name: 'Categoty 2',
-//         },
-//         {
-//             id: 3,
-//             name: 'Categoty 3',
-//         },
-//         {
-//             id: 4,
-//             name: 'Categoty 4',
-//         },
-//     ],
-// }),
-
-// .then((data) => {
-//     if (data.errors) {
-//         throw new Error(JSON.stringify(data.errors));
-//     } else return Object.values(data.data)[0];
-// })
-// fetch(backendURL + "/api/reception/patient/", {
-//     method: "GET",
-//     headers: {
-//         "Content-Type": "application/json",
-//         ...(localStorage.authToken ? { Authorization: "Bearer " + localStorage.authToken } : {}),
-//     },
-// });
-
-// actionPromise("patientAll",    fetch(url, {
-//     type:"GET",
-//     headers: {
-//         "Content-Type": "application/json",
-//         ...(localStorage.authToken ? { Authorization: "Bearer " + localStorage.authToken } : {}),
-//     },
-//     body: data,
-// })
-//     .then((res) => res.json())
-//     .then((data) => {
-//         if (typeof data === "string") {
-//             throw new Error(data);
-//         } else return Object.values(data);
-//     }))

+ 12 - 7
src/actions/actionUpdateAvatar.js

@@ -1,10 +1,15 @@
+import { call, put, select } from "redux-saga/effects";
 import { actionPromiseClear } from "../reducers";
+import { promiseWorker } from "../reducers/promiseReducer";
 import { actionAboutMe } from "./actionAboutMe";
 import { actionUploadFile } from "./actionUploadFile";
 import { actionUserUpsert } from "./actionUserUpsert";
 
-export const actionUpdateAvatar = (file) => async (dispatch, getState) => {
-    await dispatch(actionUploadFile(file));
+export const actionUpdateAvatar = (file) => ({ type: "UPDATE_AVATAR", payload: file });
+
+export function* updateAvatarWorker(action) {
+    const file = action.payload;
+    yield call(promiseWorker, actionUploadFile(file));
 
     const {
         promise: {
@@ -13,13 +18,13 @@ export const actionUpdateAvatar = (file) => async (dispatch, getState) => {
                 status,
             },
         },
-    } = getState();
+    } = yield select();
 
-    await dispatch(actionUserUpsert({ avatar: { _id } }));
+    yield call(promiseWorker, actionUserUpsert({ avatar: { _id } }));
 
     if (status === "FULFILLED") {
-        await dispatch(actionAboutMe());
+        yield put(actionAboutMe());
     }
 
-    await dispatch(actionPromiseClear("uploadFile"));
-};
+    yield put(actionPromiseClear("uploadFile"));
+}

+ 10 - 7
src/actions/actionUploadFiles.js

@@ -1,8 +1,11 @@
-import { actionUploadFile } from './actionUploadFile';
-import { actionPromise } from '../reducers';
+import { actionUploadFile } from "./actionUploadFile";
+import { actionPromise } from "../reducers";
+import { all, call, put } from "redux-saga/effects";
+import { promiseWorker } from "../reducers/promiseReducer";
 
-export const actionUploadFiles =
-    (files = []) =>
-    async (dispatch, getState) => {
-        dispatch(actionPromise('uploadFiles', Promise.all(files?.map((file) => dispatch(actionUploadFile(file))))));
-    };
+export const actionUploadFiles = (files = []) => ({ type: "UPLOAD_FILES", payload: files });
+
+export function* uploadFilesWorker(action) {
+    const files = action.payload || [];
+    yield call(promiseWorker, actionPromise("uploadFiles", yield all(files.map((file) => call(promiseWorker, actionUploadFile(file))))));
+}

+ 18 - 10
src/actions/actionUserPage.js

@@ -1,13 +1,21 @@
+import { put, take } from "redux-saga/effects";
 import { actionPromiseClear } from "../reducers";
 import { actionUserById } from "./actionUserById";
 
-export const actionUserPage =
-    ({ _id, promiseName = "userById" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(actionPromiseClear("uploadFile"));
-        if (_id) {
-            dispatch(actionUserById({ _id, promiseName }));
-        } else {
-            dispatch(actionPromiseClear(promiseName));
-        }
-    };
+export const actionUserPage = ({ _id, promiseName = "userById" } = {}) => ({ type: "USER_PAGE", payload: { _id, promiseName } });
+
+export function* userPageWorker(action) {
+    const { _id, promiseName = "userById" } = action.payload || {};
+    yield put(actionPromiseClear("uploadFile"));
+
+    if (_id) {
+        yield put(actionUserById({ _id, promiseName }));
+    } else {
+        yield put(actionPromiseClear(promiseName));
+    }
+
+    yield take("USER_PAGE_CLEAN");
+
+    yield put(actionPromiseClear(promiseName));
+    yield put(actionPromiseClear("uploadFile"));
+}

+ 0 - 8
src/actions/actionUserPageClear.js

@@ -1,8 +0,0 @@
-import { actionPromiseClear } from "../reducers";
-
-export const actionUserPageClear =
-    ({ promiseName = "userById" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(actionPromiseClear(promiseName));
-        dispatch(actionPromiseClear("uploadFile"));
-    };

+ 12 - 7
src/actions/actionUserUpdate.js

@@ -1,24 +1,29 @@
+import { call, delay, put, select } from "redux-saga/effects";
 import { actionPromiseClear } from "../reducers";
+import { promiseWorker } from "../reducers/promiseReducer";
 import { actionAboutMe } from "./actionAboutMe";
-import { actionUploadFile } from "./actionUploadFile";
 import { actionUserUpsert } from "./actionUserUpsert";
 
-export const actionUserUpdate = (user) => async (dispatch, getState) => {
-    await dispatch(actionUserUpsert(user));
+export const actionUserUpdate = (user) => ({ type: "USER_UPDATE", payload: user });
 
+export function* userUpdateWorker(action) {
+    const user = action.payload || {};
     if (!user) {
         return;
     }
 
+    yield call(promiseWorker, actionUserUpsert(user));
+
     const {
         promise: {
             userUpsert: { status },
         },
-    } = getState();
+    } = yield select();
 
     if (status === "FULFILLED") {
-        await dispatch(actionAboutMe());
+        yield put(actionAboutMe());
     }
 
-    await dispatch(actionPromiseClear("userUpsert"));
-};
+    yield delay(500);
+    yield put(actionPromiseClear("userUpsert"));
+}

+ 8 - 10
src/actions/actionUserUpsert.js

@@ -1,24 +1,22 @@
 import { gql } from "../helpers";
 import { actionPromise } from "../reducers";
 
-export const actionUserUpsert = (user) => async (dispatch, getState) => {
+export const actionUserUpsert = (user) => {
     if (!user?.password?.length) {
         delete user.password;
     }
 
-    await dispatch(
-        actionPromise(
-            "userUpsert",
-            gql(
-                `mutation userUpsert($user:UserInput!){
+    return actionPromise(
+        "userUpsert",
+        gql(
+            `mutation userUpsert($user:UserInput!){
               UserUpsert(user:$user){
                   _id username 
               }
           }`,
-                {
-                    user,
-                }
-            )
+            {
+                user,
+            }
         )
     );
 };

+ 16 - 21
src/actions/actionUsersAll.js

@@ -1,14 +1,11 @@
 import { actionPromise } from "../reducers";
 import { gql } from "../helpers";
 
-export const actionUsersAll =
-    ({ limit = 0, skip = 0, promiseName = "adminUsersAll", orderBy = "_id" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(
-            actionPromise(
-                promiseName,
-                gql(
-                    `query UsersAll($query:String){
+export const actionUsersAll = ({ limit = 0, skip = 0, promiseName = "adminUsersAll", orderBy = "_id" } = {}) =>
+    actionPromise(
+        promiseName,
+        gql(
+            `query UsersAll($query:String){
                         UserFind(query: $query){
                             _id username is_active acl
                             avatar{
@@ -16,17 +13,15 @@ export const actionUsersAll =
                             }
                         }
                     }`,
+            {
+                query: JSON.stringify([
+                    {},
                     {
-                        query: JSON.stringify([
-                            {},
-                            {
-                                limit: !!limit ? limit : 100,
-                                skip: skip,
-                                orderBy,
-                            },
-                        ]),
-                    }
-                )
-            )
-        );
-    };
+                        limit: !!limit ? limit : 100,
+                        skip: skip,
+                        orderBy,
+                    },
+                ]),
+            }
+        )
+    );

+ 19 - 24
src/actions/actionUsersFind.js

@@ -1,14 +1,11 @@
 import { actionPromise } from "../reducers";
 import { gql } from "../helpers";
 
-export const actionUsersFind =
-    ({ text = "", limit = 0, skip = 0, promiseName = "adminUsersFind", orderBy = "_id" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(
-            actionPromise(
-                promiseName,
-                gql(
-                    `query UsersFind($query:String){
+export const actionUsersFind = ({ text = "", limit = 0, skip = 0, promiseName = "adminUsersFind", orderBy = "_id" } = {}) =>
+    actionPromise(
+        promiseName,
+        gql(
+            `query UsersFind($query:String){
                         UserFind(query: $query){
                             _id username acl is_active
                             avatar{
@@ -16,20 +13,18 @@ export const actionUsersFind =
                             }
                         }
                     }`,
+            {
+                query: JSON.stringify([
                     {
-                        query: JSON.stringify([
-                            {
-                                username__contains: text,
-                                _id__contains: text,
-                            },
-                            {
-                                limit: !!limit ? limit : 100,
-                                skip: skip,
-                                orderBy,
-                            },
-                        ]),
-                    }
-                )
-            )
-        );
-    };
+                        username__contains: text,
+                        _id__contains: text,
+                    },
+                    {
+                        limit: !!limit ? limit : 100,
+                        skip: skip,
+                        orderBy,
+                    },
+                ]),
+            }
+        )
+    );

+ 13 - 7
src/actions/actionUsersPage.js

@@ -1,9 +1,15 @@
+import { put, take } from "redux-saga/effects";
 import { actionFeedClear, actionFeedUsers, actionPromiseClear } from "../reducers";
 
-export const actionUsersPage =
-    ({ orderBy = "_id" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(actionFeedClear());
-        dispatch(actionPromiseClear("feedUsersAll"));
-        dispatch(actionFeedUsers({ skip: 0, orderBy }));
-    };
+export const actionUsersPage = ({ orderBy = "_id" } = {}) => ({ type: "USERS_PAGE", payload: { orderBy } });
+
+export function* usersPageWorker(action) {
+    const { orderBy = "_id" } = action.payload || {};
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedUsersAll"));
+    yield put(actionFeedUsers({ skip: 0, orderBy }));
+    yield take("USERS_PAGE_CLEAN");
+
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedUsersAll"));
+}

+ 0 - 6
src/actions/actionUsersPageClear.js

@@ -1,6 +0,0 @@
-import { actionFeedClear, actionPromiseClear } from "../reducers";
-
-export const actionUsersPageClear = () => async (dispatch, getState) => {
-    dispatch(actionFeedClear());
-    dispatch(actionPromiseClear("feedUsersAll"));
-};

+ 13 - 7
src/actions/actionUsersSearchPage.js

@@ -1,9 +1,15 @@
+import { put, take } from "redux-saga/effects";
 import { actionFeedClear, actionFeedUsersFind, actionPromiseClear } from "../reducers";
 
-export const actionUsersSearchPage =
-    ({ orderBy = "_id", text = "" } = {}) =>
-    async (dispatch, getState) => {
-        dispatch(actionFeedClear());
-        dispatch(actionPromiseClear("feedUsersFind"));
-        dispatch(actionFeedUsersFind({ skip: 0, orderBy, text }));
-    };
+export const actionUsersSearchPage = ({ orderBy = "_id", text = "" } = {}) => ({ type: "USERS_SEARCH_PAGE", payload: { orderBy, text } });
+
+export function* usersSearchPageWorker(action) {
+    const { orderBy = "_id", text = "" } = action.payload || {};
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedUsersFind"));
+    yield put(actionFeedUsersFind({ skip: 0, orderBy, text }));
+
+    yield take("USERS_SEARCH_PAGE_CLEAR");
+    yield put(actionFeedClear());
+    yield put(actionPromiseClear("feedUsersFind"));
+}

+ 0 - 6
src/actions/actionUsersSearchPageClear.js

@@ -1,6 +0,0 @@
-import { actionFeedClear, actionPromiseClear } from "../reducers";
-
-export const actionUsersSearchPageClear = () => async (dispatch, getState) => {
-    dispatch(actionFeedClear());
-    dispatch(actionPromiseClear("feedUsersFind"));
-};

+ 6 - 5
src/components/CartPage/CartItem.js

@@ -4,16 +4,15 @@ import { IoCloseOutline } from "react-icons/io5";
 import { AiOutlinePlus, AiOutlineMinus } from "react-icons/ai";
 import { actionCartChange } from "../../reducers";
 import { useEffect, useState } from "react";
-import { useDispatch } from "react-redux";
 import { backendURL, mediaURL } from "../../helpers";
 
-const { Typography, Stack, IconButton, TextField, ButtonGroup, Button, TableCell, TableRow, Input } = require("@mui/material");
+import { Typography, Stack, IconButton, TableCell, TableRow } from "@mui/material";
+import { connect } from "react-redux";
 
-export const CartItem = ({ order, onDeleteClick }) => {
+export const CartItem = ({ order, onDeleteClick, onChange }) => {
     const { good, count = 1 } = order || {};
     const { _id, images = [], name = "", price = 0, amount = 1 } = good;
 
-    const dispatch = useDispatch();
     const [countInput, setCountInput] = useState(count || 1);
 
     useEffect(() => {
@@ -21,7 +20,7 @@ export const CartItem = ({ order, onDeleteClick }) => {
     }, [count]);
 
     useEffect(() => {
-        dispatch(actionCartChange(good, +countInput));
+        onChange(good, +countInput);
     }, [countInput]);
 
     const handleChange = (count) => {
@@ -75,3 +74,5 @@ export const CartItem = ({ order, onDeleteClick }) => {
         </TableRow>
     );
 };
+
+export const CCartItem = connect(null, { onChange: (good, countInput) => actionCartChange(good, +countInput) })(CartItem);

+ 16 - 7
src/components/CartPage/index.js

@@ -1,23 +1,24 @@
 import { Box, Button, Stack, Table, TableBody, TableCell, TableRow, Typography } from "@mui/material";
 import { useFormik } from "formik";
-import { useContext, useEffect } from "react";
-import { connect, useDispatch, useSelector } from "react-redux";
+import { useContext, useEffect, useState } from "react";
+import { connect, useSelector } from "react-redux";
 import { useNavigate } from "react-router-dom";
 import { actionOrderUpdate } from "../../actions/actionOrderUpdate";
 import { actionCartDelete } from "../../reducers";
 import { UIContext } from "../UIContext";
-import { CartItem } from "./CartItem";
+import { CCartItem } from "./CartItem";
 
-export const CartPage = ({ onConfirm, promiseStatus, serverErrors }) => {
+export const CartPage = ({ onConfirm, promiseStatus, serverErrors, onDeleteClick }) => {
     const cart = useSelector((state) => state.cart || {});
     const { setAlert } = useContext(UIContext);
     const sum = Object.entries(cart).reduce((prev, [_id, order]) => prev + order.count * order.good.price, 0);
-    const dispatch = useDispatch();
+    const [promiseTimeOut, setPromiseTimeOut] = useState(null);
     const navigate = useNavigate();
 
     const formik = useFormik({
         initialValues: {},
         onSubmit: () => {
+            setPromiseTimeOut(setTimeout(() => formik.setSubmitting(false), 3000));
             onConfirm && Object.keys(cart).length && onConfirm({ orderGoods: Object.values(cart) });
         },
     });
@@ -26,6 +27,9 @@ export const CartPage = ({ onConfirm, promiseStatus, serverErrors }) => {
         if (!Object.entries(cart).length) {
             navigate("/");
         }
+        return () => {
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+        };
     }, []);
 
     useEffect(() => {
@@ -35,6 +39,8 @@ export const CartPage = ({ onConfirm, promiseStatus, serverErrors }) => {
     useEffect(() => {
         if (promiseStatus === "FULFILLED") {
             formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
             setAlert({
                 show: true,
                 severity: "success",
@@ -42,8 +48,10 @@ export const CartPage = ({ onConfirm, promiseStatus, serverErrors }) => {
             });
         }
         if (promiseStatus === "REJECTED") {
-            const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
+            const errorMessage = (serverErrors ? [].concat(serverErrors) : []).reduce((prev, curr) => prev + "\n" + curr.message, "");
             formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
             setAlert({
                 show: true,
                 severity: "error",
@@ -59,7 +67,7 @@ export const CartPage = ({ onConfirm, promiseStatus, serverErrors }) => {
                 <Table className="table">
                     <TableBody>
                         {Object.entries(cart).map(([_id, order]) => (
-                            <CartItem order={order} onDeleteClick={(good) => dispatch(actionCartDelete(good))} key={_id} />
+                            <CCartItem order={order} onDeleteClick={(good) => onDeleteClick(good)} key={_id} />
                         ))}
 
                         <TableRow>
@@ -96,5 +104,6 @@ export const CCartPage = connect(
     }),
     {
         onConfirm: (order) => actionOrderUpdate(order),
+        onDeleteClick: (good) => actionCartDelete(good),
     }
 )(CartPage);

+ 1 - 1
src/components/DashboardPage/ProfileForm/index.js

@@ -70,7 +70,7 @@ export const ProfileForm = ({ profile = {}, promiseStatus, onProfileSave, server
             });
         }
         if (promiseStatus === "REJECTED") {
-            const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
+            const errorMessage = (serverErrors ? [].concat(serverErrors) : []).reduce((prev, curr) => prev + "\n" + curr.message, "");
             formik.setSubmitting(false);
             promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);

+ 1 - 1
src/components/GoodPage/index.js

@@ -14,7 +14,7 @@ export const GoodPage = () => {
             <Grid container spacing={4} className="images">
                 <Grid item xs={12} md={4}>
                     <Carousel showIndicators={false} showStatus={false} showArrows={true}>
-                        {(good.images || [{ _id: 0, url: defaultGoodImage }]).map((image) => (
+                        {(!!good?.images?.length ? good?.images : [{ _id: 0, url: defaultGoodImage }]).map((image) => (
                             <img
                                 key={image?._id}
                                 src={image?.url ? `${backendURL}${mediaURL}${image?.url}` : defaultGoodImage}

+ 1 - 1
src/components/GoodsPage/index.js

@@ -22,7 +22,7 @@ const GoodsPage = ({ category = {}, goods = [] }) => {
             <Stack>
                 <Box className="sortOptionsWrapper">
                     <SortOptions
-                        defaultOption={searchParams.get("orderBy", null)}
+                        defaultOption={searchParams.get("orderBy", "createdAt")}
                         onClick={(option) => {
                             searchParams.set("orderBy", option.value);
                             setSearchParams(searchParams);

+ 2 - 4
src/components/LayoutPage/GoodsPageContainer.js

@@ -1,9 +1,7 @@
 import { connect } from "react-redux";
 import { useEffect } from "react";
-import { useDispatch } from "react-redux";
 import { useParams, useSearchParams } from "react-router-dom";
 import { actionCatByIdFull } from "../../actions/actionCatByIdFull";
-import { actionCatByIdFullClear } from "../../actions/actionCatByIdFullClear";
 import { actionFeedCategoryGoods } from "../../reducers/feedReducer";
 import { InfScroll } from "../common/InfScroll";
 import { CGoodsPage } from "../GoodsPage";
@@ -17,7 +15,7 @@ const GoodsPageContainer = ({ feed, goods, promiseStatus, onLoad, onUnmount, onS
         onLoad({ orderBy, _id: params._id });
 
         return () => {
-            onUnmount();
+            onUnmount && onUnmount();
         };
     }, [params._id, orderBy]);
 
@@ -40,7 +38,7 @@ export const CAdminGoodsPageContainer = connect(
         promiseStatus: state.promise?.feedCategoryGoods?.status || null,
     }),
     {
-        onUnmount: () => actionCatByIdFullClear(),
+        onUnmount: () => ({ type: "CAT_BY_ID_FULL_CLEAR" }),
         onLoad: ({ orderBy, _id }) => actionCatByIdFull({ orderBy, _id }),
         onScroll: ({ feed, orderBy, category }) => actionFeedCategoryGoods({ skip: feed.length || 0, orderBy, category }),
     }

+ 1 - 2
src/components/LayoutPage/GoodsSearchPageContainer.js

@@ -2,7 +2,6 @@ import { useEffect } from "react";
 import { connect } from "react-redux";
 import { useSearchParams } from "react-router-dom";
 import { actionGoodsSearchPage } from "../../actions/actionGoodsSearchPage";
-import { actionGoodsSearchPageClear } from "../../actions/actionGoodsSearchPageClear";
 import { actionFeedGoodsFind } from "../../reducers";
 import { InfScroll } from "../common/InfScroll";
 import { CGoodsPage } from "../GoodsPage";
@@ -37,7 +36,7 @@ export const CAdminGoodsSearchPageContainer = connect(
         promiseStatus: state.promise?.feedGoodsFind?.status || null,
     }),
     {
-        onUnmount: () => actionGoodsSearchPageClear(),
+        onUnmount: () => ({ type: "GOOD_SEARCH_PAGE_CLEAR" }),
         onLoad: ({ orderBy, text }) => actionGoodsSearchPage({ orderBy, text }),
         onScroll: ({ feed, orderBy, text }) => actionFeedGoodsFind({ text, skip: feed?.length || 0, orderBy }),
     }

+ 15 - 13
src/components/LayoutPage/index.js

@@ -7,7 +7,6 @@ import { actionGoodsPopular } from "../../actions/actionGoodsPopular";
 import { actionOrders } from "../../actions/actionOrders";
 import { AdminLayoutPage } from "../admin/AdminLayoutPage";
 import { CCartPage } from "../CartPage";
-import { GoodList } from "../common/GoodList";
 import { CProtectedRoute } from "../common/ProtectedRoute";
 import { CDashboardPage } from "../DashboardPage";
 import { GoodPage } from "../GoodPage";
@@ -19,10 +18,13 @@ import { MainPage } from "../MainPage";
 import { CAdminGoodsPageContainer } from "./GoodsPageContainer";
 import { CAdminGoodsSearchPageContainer } from "./GoodsSearchPageContainer";
 
-const GoodPageContainer = ({ onLoad }) => {
+const GoodPageContainer = ({ onLoad, onUnmount }) => {
     const params = useParams();
     useEffect(() => {
         onLoad({ _id: params._id });
+        return () => {
+            onUnmount && onUnmount();
+        };
     }, []);
 
     return <GoodPage />;
@@ -44,21 +46,21 @@ const CDashboardPageContainer = connect(null, {
     onLoad: () => actionOrders(),
 })(DashboardPageContainer);
 
-const CGoodsList = connect((state) => ({ goods: state.promise?.pageGoodsFind?.payload || [] }))(GoodList);
+// const CGoodsList = connect((state) => ({ goods: state.promise?.pageGoodsFind?.payload || [] }))(GoodList);
 
-const GoodsListContainer = ({ onLoad }) => {
-    const params = useParams();
+// const GoodsListContainer = ({ onLoad }) => {
+//     const params = useParams();
 
-    useEffect(() => {
-        onLoad({ text: params.searchData, promiseName: "pageGoodsFind" });
-    }, [params.searchData]);
+//     useEffect(() => {
+//         onLoad({ text: params.searchData, promiseName: "pageGoodsFind" });
+//     }, [params.searchData]);
 
-    return <CGoodsList />;
-};
+//     return <CGoodsList />;
+// };
 
-const CGoodsListContainer = connect(null, {
-    onLoad: ({ text, promiseName }) => actionOrders({ text, promiseName }),
-})(GoodsListContainer);
+// const CGoodsListContainer = connect(null, {
+//     onLoad: ({ text, promiseName }) => actionOrders({ text, promiseName }),
+// })(GoodsListContainer);
 
 const MainPageContainer = ({ onLoad, goods }) => {
     useEffect(() => {

+ 9 - 6
src/components/Root/index.js

@@ -1,18 +1,19 @@
 import { Navigate, Route, Routes } from "react-router-dom";
 
 import { Box } from "@mui/material";
-import styles from "react-responsive-carousel/lib/styles/carousel.min.css";
 import { actionPageStart } from "../../actions/actionPageStart";
-import { useDispatch } from "react-redux";
-
+import "react-responsive-carousel/lib/styles/carousel.min.css";
 import { AuthPage } from "../AuthPage";
 import { LayoutPage } from "../LayoutPage";
 import { CProtectedRoute } from "../common/ProtectedRoute";
 import { Error404 } from "../common/Error404";
+import { useEffect } from "react";
+import { connect } from "react-redux";
 
-const Root = () => {
-    const dispatch = useDispatch();
-    dispatch(actionPageStart());
+const Root = ({ onLoad }) => {
+    useEffect(() => {
+        onLoad();
+    }, []);
 
     return (
         <Box className="Root">
@@ -33,4 +34,6 @@ const Root = () => {
     );
 };
 
+export const CRoot = connect(null, { onLoad: () => actionPageStart() })(Root);
+
 export { Root };

+ 4 - 8
src/components/admin/AdminCategoryPage/CategoryForm.js

@@ -1,8 +1,7 @@
-import { connect, useDispatch } from "react-redux";
+import { connect } from "react-redux";
 import { useState, useEffect, useContext } from "react";
 import Select from "react-select";
 import { actionCategoryUpdate } from "../../../actions/actionCategoryUpdate";
-import { actionPromiseClear } from "../../../reducers";
 import { Box, Button, InputLabel, Stack, TextField } from "@mui/material";
 import { UIContext } from "../../UIContext";
 import { useFormik } from "formik";
@@ -10,6 +9,7 @@ import * as Yup from "yup";
 import { ConfirmModal } from "../../common/ConfirmModal";
 import { useNavigate } from "react-router-dom";
 import { actionCategoryDelete } from "../../../actions/actionCategoryDelete";
+import { actionPromisesClear } from "../../../actions/actionPromisesClear";
 
 const categorySchema = Yup.object().shape({
     name: Yup.string().required("Обов'язкове"),
@@ -36,7 +36,6 @@ const CategoryForm = ({
     const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
     const [promiseTimeOut, setPromiseTimeOut] = useState(null);
     const navigate = useNavigate();
-    const dispatch = useDispatch();
 
     const formik = useFormik({
         initialValues: {
@@ -86,7 +85,7 @@ const CategoryForm = ({
             });
         }
         if (promiseStatus === "REJECTED") {
-            const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
+            const errorMessage = (serverErrors ? [].concat(serverErrors) : []).reduce((prev, curr) => prev + "\n" + curr.message, "");
             formik.setSubmitting(false);
             promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);
@@ -113,9 +112,6 @@ const CategoryForm = ({
                 message: "Помилка",
             });
         }
-        return () => {
-            dispatch(actionPromiseClear("categoryDelete"));
-        };
     }, [deletePromiseStatus]);
 
     useEffect(() => {
@@ -229,7 +225,7 @@ export const CCategoryForm = connect(
     }),
     {
         onSave: (cat) => actionCategoryUpdate(cat),
-        onClose: () => actionPromiseClear("categoryUpsert"),
+        onClose: () => actionPromisesClear(["categoryUpsert", "categoryDelete"]),
         onDelete: (category) => actionCategoryDelete({ category }),
     }
 )(CategoryForm);

+ 5 - 6
src/components/admin/AdminGoodPage/GoodForm.js

@@ -1,4 +1,4 @@
-import { connect, useDispatch } from "react-redux";
+import { connect } from "react-redux";
 import { useState, useEffect, useContext } from "react";
 import { actionPromiseClear } from "../../../reducers";
 import Select from "react-select";
@@ -36,6 +36,7 @@ export const GoodForm = ({
     onSave,
     onClose,
     onDelete,
+
     promiseStatus,
     deletePromiseStatus,
     catList = [],
@@ -47,7 +48,6 @@ export const GoodForm = ({
     const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
     const [promiseTimeOut, setPromiseTimeOut] = useState(null);
     const navigate = useNavigate();
-    const dispatch = useDispatch();
 
     const formik = useFormik({
         initialValues: {
@@ -92,7 +92,8 @@ export const GoodForm = ({
             });
         }
         if (promiseStatus === "REJECTED") {
-            const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
+            console.log(serverErrors);
+            const errorMessage = (serverErrors ? [].concat(serverErrors) : []).reduce((prev, curr) => prev + "\n" + curr.message, "");
             formik.setSubmitting(false);
             promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);
@@ -119,9 +120,7 @@ export const GoodForm = ({
                 message: "Помилка",
             });
         }
-        return () => {
-            dispatch(actionPromiseClear("goodDelete"));
-        };
+        return () => {};
     }, [deletePromiseStatus]);
 
     useEffect(() => {

+ 12 - 23
src/components/admin/AdminLayoutPage/AdminCategoryLayout/AdminCategoriesPageContainer.js

@@ -1,14 +1,12 @@
 import { connect } from "react-redux";
 import { useEffect } from "react";
-import { useDispatch } from "react-redux";
 import { useSearchParams } from "react-router-dom";
 import { actionCategoriesPage } from "../../../../actions/actionCategoriesPage";
-import { actionCategoriesPageClear } from "../../../../actions/actionCategoriesPageClear";
-import { actionFeedAdd, actionFeedCats } from "../../../../reducers";
+import { actionFeedCats } from "../../../../reducers";
 import { AdminCategoriesPage } from "../../AdminCategoriesPage";
+import { InfScroll } from "../../../common/InfScroll";
 
 const AdminCategoriesPageContainer = ({ feed, cats, promiseStatus, onLoad, onUnmount, onScroll }) => {
-    const dispatch = useDispatch();
     const [searchParams] = useSearchParams();
     const orderBy = searchParams.get("orderBy") || "_id";
 
@@ -19,24 +17,15 @@ const AdminCategoriesPageContainer = ({ feed, cats, promiseStatus, onLoad, onUnm
         };
     }, [orderBy]);
 
-    useEffect(() => {
-        window.onscroll = (e) => {
-            if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 200) {
-                if (promiseStatus !== "PENDING") {
-                    onScroll({ feed, orderBy });
-                }
-            }
-        };
-        return () => {
-            window.onscroll = null;
-        };
-    }, [feed, promiseStatus]);
-
-    useEffect(() => {
-        if (cats.length) dispatch(actionFeedAdd(cats));
-    }, [cats]);
-
-    return <AdminCategoriesPage orderBy={orderBy} />;
+    return (
+        <InfScroll
+            items={cats}
+            component={AdminCategoriesPage}
+            promiseStatus={promiseStatus}
+            onScroll={() => onScroll({ feed, orderBy })}
+            orderBy={orderBy}
+        />
+    );
 };
 
 export const CAdminCategoriesPageContainer = connect(
@@ -46,7 +35,7 @@ export const CAdminCategoriesPageContainer = connect(
         promiseStatus: state.promise?.feedCatAll?.status || null,
     }),
     {
-        onUnmount: () => actionCategoriesPageClear(),
+        onUnmount: () => ({ type: "CATEGORIES_PAGE_CLEAR" }),
         onLoad: ({ orderBy }) => actionCategoriesPage({ orderBy }),
         onScroll: ({ feed, orderBy }) => actionFeedCats({ skip: feed?.length || 0, orderBy }),
     }

+ 12 - 23
src/components/admin/AdminLayoutPage/AdminCategoryLayout/AdminCategoriesSearchPageContainer.js

@@ -1,14 +1,12 @@
 import { connect } from "react-redux";
 import { useEffect } from "react";
-import { useDispatch } from "react-redux";
 import { useSearchParams } from "react-router-dom";
 import { actionCategoriesSearchPage } from "../../../../actions/actionCategoriesSearchPage";
-import { actionCategoriesSearchPageClear } from "../../../../actions/actionCategoriesSearchPageClear";
-import { actionFeedAdd, actionFeedCatsFind } from "../../../../reducers";
+import { actionFeedCatsFind } from "../../../../reducers";
 import { AdminCategoriesPage } from "../../AdminCategoriesPage";
+import { InfScroll } from "../../../common/InfScroll";
 
 const AdminCategoriesSearchPageContainer = ({ feed, cats, promiseStatus, onLoad, onUnmount, onScroll }) => {
-    const dispatch = useDispatch();
     const [searchParams] = useSearchParams();
     const orderBy = searchParams.get("orderBy") || "_id";
     const text = searchParams.get("text") || "";
@@ -20,24 +18,15 @@ const AdminCategoriesSearchPageContainer = ({ feed, cats, promiseStatus, onLoad,
         };
     }, [orderBy, text]);
 
-    useEffect(() => {
-        window.onscroll = (e) => {
-            if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 200) {
-                if (promiseStatus !== "PENDING") {
-                    onScroll({ feed, orderBy, text });
-                }
-            }
-        };
-        return () => {
-            window.onscroll = null;
-        };
-    }, [promiseStatus, feed, text]);
-
-    useEffect(() => {
-        if (cats?.length) dispatch(actionFeedAdd(cats));
-    }, [cats]);
-
-    return <AdminCategoriesPage orderBy={orderBy} />;
+    return (
+        <InfScroll
+            items={cats}
+            component={AdminCategoriesPage}
+            promiseStatus={promiseStatus}
+            onScroll={() => onScroll({ feed, orderBy, text })}
+            orderBy={orderBy}
+        />
+    );
 };
 
 export const CAdminCategoriesSearchPageContainer = connect(
@@ -47,7 +36,7 @@ export const CAdminCategoriesSearchPageContainer = connect(
         promiseStatus: state.promise?.feedCatsFind?.status || null,
     }),
     {
-        onUnmount: () => actionCategoriesSearchPageClear(),
+        onUnmount: () => ({ type: "CATEGORIES_SEARCH_PAGE_CLEAR" }),
         onLoad: ({ orderBy, text }) => actionCategoriesSearchPage({ orderBy, text }),
         onScroll: ({ feed, orderBy, text }) => actionFeedCatsFind({ text, skip: feed?.length || 0, orderBy }),
     }

+ 1 - 2
src/components/admin/AdminLayoutPage/AdminCategoryLayout/AdminCategoryPageContainer.js

@@ -2,7 +2,6 @@ import { connect } from "react-redux";
 import { useEffect } from "react";
 import { useParams } from "react-router-dom";
 import { actionCategoryPage } from "../../../../actions/actionCategoryPage";
-import { actionCategoryPageClear } from "../../../../actions/actionCategoryPageClear";
 import { CAdminCategoryPage } from "../../AdminCategoryPage";
 
 const AdminCategoryPageContainer = ({ onUnmount, onLoad }) => {
@@ -19,6 +18,6 @@ const AdminCategoryPageContainer = ({ onUnmount, onLoad }) => {
 };
 
 export const CAdminCategoryPageContainer = connect(null, {
-    onUnmount: () => actionCategoryPageClear({ promiseName: "adminCatById" }),
+    onUnmount: () => ({ type: "CATEGORY_PAGE_CLEAR" }),
     onLoad: (_id) => actionCategoryPage({ _id, promiseName: "adminCatById" }),
 })(AdminCategoryPageContainer);

+ 1 - 2
src/components/admin/AdminLayoutPage/AdminGoodLayout/AdminGoodPageContainer.js

@@ -1,6 +1,5 @@
 import { CAdminGoodPage } from "../../AdminGoodPage";
 import { actionGoodPage } from "../../../../actions/actionGoodPage";
-import { actionGoodPageClear } from "../../../../actions/actionGoodPageClear";
 import { useParams } from "react-router-dom";
 import { useEffect } from "react";
 import { connect } from "react-redux";
@@ -19,6 +18,6 @@ const AdminGoodPageContainer = ({ onUnmount, onLoad }) => {
 };
 
 export const CAdminGoodPageContainer = connect(null, {
-    onUnmount: () => actionGoodPageClear({ promiseName: "adminGoodById" }),
+    onUnmount: () => ({ type: "GOOD_PAGE_CLEAR" }),
     onLoad: (_id) => actionGoodPage({ _id, promiseName: "adminGoodById" }),
 })(AdminGoodPageContainer);

+ 3 - 3
src/components/admin/AdminLayoutPage/AdminGoodLayout/AdminGoodsPageContainer.js

@@ -1,4 +1,3 @@
-import { actionGoodsPageClear } from "../../../../actions/actionGoodsPageClear";
 import { actionGoodsPage } from "../../../../actions/actionGoodsPage";
 import { useSearchParams } from "react-router-dom";
 import { useEffect } from "react";
@@ -13,8 +12,9 @@ const AdminGoodsPageContainer = ({ feed, goods, promiseStatus, onLoad, onUnmount
 
     useEffect(() => {
         onLoad({ orderBy });
+
         return () => {
-            onUnmount();
+            onUnmount && onUnmount();
         };
     }, [orderBy]);
 
@@ -36,7 +36,7 @@ export const CAdminGoodsPageContainer = connect(
         promiseStatus: state.promise?.feedGoodsAll?.status || null,
     }),
     {
-        onUnmount: () => actionGoodsPageClear(),
+        onUnmount: () => ({ type: "GOODS_PAGE_CLEAR" }),
         onLoad: ({ orderBy }) => actionGoodsPage({ orderBy }),
         onScroll: ({ feed, orderBy }) => actionFeedGoods({ skip: feed?.length || 0, orderBy }),
     }

+ 1 - 2
src/components/admin/AdminLayoutPage/AdminGoodLayout/AdminGoodsSearchPageContainer.js

@@ -2,7 +2,6 @@ import { connect } from "react-redux";
 import { useEffect } from "react";
 import { useSearchParams } from "react-router-dom";
 import { actionGoodsSearchPage } from "../../../../actions/actionGoodsSearchPage";
-import { actionGoodsSearchPageClear } from "../../../../actions/actionGoodsSearchPageClear";
 import { actionFeedGoodsFind } from "../../../../reducers";
 import { InfScroll } from "../../../common/InfScroll";
 import { AdminGoodsPage } from "../../AdminGoodsPage";
@@ -37,7 +36,7 @@ export const CAdminGoodsSearchPageContainer = connect(
         promiseStatus: state.promise?.feedGoodsFind?.status || null,
     }),
     {
-        onUnmount: () => actionGoodsSearchPageClear(),
+        onUnmount: () => ({ type: "GOODS_SEARCH_CLEAR" }),
         onLoad: ({ orderBy, text }) => actionGoodsSearchPage({ orderBy, text }),
         onScroll: ({ feed, orderBy, text }) => actionFeedGoodsFind({ text, skip: feed?.length || 0, orderBy }),
     }

+ 1 - 2
src/components/admin/AdminLayoutPage/AdminOrderLayout/AdminOrderPageContainer.js

@@ -2,7 +2,6 @@ import { connect } from "react-redux";
 import { useEffect } from "react";
 import { useParams } from "react-router-dom";
 import { actionOrderPage } from "../../../../actions/actionOrderPage";
-import { actionOrderPageClear } from "../../../../actions/actionOrderPageClear";
 import { CAdminOrderPage } from "../../AdminOrderPage";
 
 const AdminOrderPageContainer = ({ onLoad, onUnmount }) => {
@@ -19,6 +18,6 @@ const AdminOrderPageContainer = ({ onLoad, onUnmount }) => {
 };
 
 export const CAdminOrderPageContainer = connect(null, {
-    onUnmount: () => actionOrderPageClear({ promiseName: "adminOrderById" }),
+    onUnmount: () => ({ type: "ORDER_PAGE_CLEAR" }),
     onLoad: (_id) => actionOrderPage({ _id, promiseName: "adminOrderById" }),
 })(AdminOrderPageContainer);

+ 2 - 3
src/components/admin/AdminLayoutPage/AdminOrderLayout/AdminOrdersPageContainer.js

@@ -2,7 +2,6 @@ import { connect } from "react-redux";
 import { useEffect } from "react";
 import { useSearchParams } from "react-router-dom";
 import { actionOrdersPage } from "../../../../actions/actionOrdersPage";
-import { actionOrdersPageClear } from "../../../../actions/actionOrdersPageClear";
 import { actionFeedOrders } from "../../../../reducers";
 import { InfScroll } from "../../../common/InfScroll";
 import { AdminOrdersPage } from "../../AdminOrdersPage";
@@ -24,7 +23,7 @@ const AdminOrdersPageContainer = ({ feed, orders, promiseStatus, onLoad, onUnmou
             items={orders}
             component={AdminOrdersPage}
             promiseStatus={promiseStatus}
-            onScroll={() => onScroll({ feed, orderBy })}
+            onScroll={() => onScroll({ feed, orderBy, status })}
             orderBy={orderBy}
         />
     );
@@ -37,7 +36,7 @@ export const CAdminOrdersPageContainer = connect(
         promiseStatus: state.promise?.feedOrdersAll?.status || null,
     }),
     {
-        onUnmount: () => actionOrdersPageClear(),
+        onUnmount: () => ({ type: "ORDERS_PAGE_CLEAR" }),
         onLoad: ({ orderBy, status }) => actionOrdersPage({ orderBy, status }),
         onScroll: ({ feed, orderBy, status }) => actionFeedOrders({ skip: feed?.length || 0, orderBy, status }),
     }

+ 4 - 4
src/components/admin/AdminLayoutPage/AdminOrderLayout/AdminOrdersSearchPageContainer.js

@@ -1,10 +1,10 @@
 import { connect } from "react-redux";
 import { useEffect } from "react";
 import { useSearchParams } from "react-router-dom";
-import { actionOrdersSearchPageClear } from "../../../../actions/actionOrdersSearchPageClear";
 import { actionFeedOrdersFind } from "../../../../reducers";
 import { InfScroll } from "../../../common/InfScroll";
 import { AdminOrdersPage } from "../../AdminOrdersPage";
+import { actionOrdersSearchPage } from "../../../../actions/actionOrdersSearchPage";
 
 const AdminOrdersSearchPageContainer = ({ feed, orders, promiseStatus, onLoad, onUnmount, onScroll }) => {
     const [searchParams] = useSearchParams();
@@ -24,7 +24,7 @@ const AdminOrdersSearchPageContainer = ({ feed, orders, promiseStatus, onLoad, o
             items={orders}
             component={AdminOrdersPage}
             promiseStatus={promiseStatus}
-            onScroll={() => onScroll({ feed, orderBy })}
+            onScroll={() => onScroll({ feed, orderBy, status, text })}
             orderBy={orderBy}
         />
     );
@@ -37,8 +37,8 @@ export const CAdminOrdersSearchPageContainer = connect(
         promiseStatus: state.promise?.feedOrdersFind?.status || null,
     }),
     {
-        onUnmount: () => actionOrdersSearchPageClear(),
-        onLoad: ({ orderBy, text, status }) => actionOrdersSearchPageClear({ orderBy, text, status }),
+        onUnmount: () => ({ type: "ORDERS_SEARCH_PAGE_CLEAR" }),
+        onLoad: ({ orderBy, text, status }) => actionOrdersSearchPage({ orderBy, text, status }),
         onScroll: ({ feed, orderBy, text, status }) => actionFeedOrdersFind({ text, skip: feed?.length || 0, orderBy, status }),
     }
 )(AdminOrdersSearchPageContainer);

+ 1 - 2
src/components/admin/AdminLayoutPage/AdminUserLayout/AdminUserPageContainer.js

@@ -2,7 +2,6 @@ import { connect } from "react-redux";
 import { useEffect } from "react";
 import { useParams } from "react-router-dom";
 import { actionUserPage } from "../../../../actions/actionUserPage";
-import { actionUserPageClear } from "../../../../actions/actionUserPageClear";
 import { CAdminUserPage } from "../../AdminUserPage.js";
 
 const AdminUserPageContainer = ({ onLoad, onUnmount }) => {
@@ -21,6 +20,6 @@ const AdminUserPageContainer = ({ onLoad, onUnmount }) => {
 };
 
 export const CAdminUserPageContainer = connect(null, {
-    onUnmount: () => actionUserPageClear({ promiseName: "adminUserById" }),
+    onUnmount: () => ({ type: "USER_PAGE_CLEAN" }),
     onLoad: (_id) => actionUserPage({ _id, promiseName: "adminUserById" }),
 })(AdminUserPageContainer);

+ 1 - 2
src/components/admin/AdminLayoutPage/AdminUserLayout/AdminUsersPageContainer.js

@@ -2,7 +2,6 @@ import { connect } from "react-redux";
 import { useEffect } from "react";
 import { useSearchParams } from "react-router-dom";
 import { actionUsersPage } from "../../../../actions/actionUsersPage";
-import { actionUsersPageClear } from "../../../../actions/actionUsersPageClear";
 import { actionFeedUsers } from "../../../../reducers";
 import { InfScroll } from "../../../common/InfScroll";
 import { AdminUsersPage } from "../../AdminUsersPage";
@@ -36,7 +35,7 @@ export const CAdminUsersPageContainer = connect(
         promiseStatus: state.promise?.feedUsersAll?.status || null,
     }),
     {
-        onUnmount: () => actionUsersPageClear(),
+        onUnmount: () => ({ type: "USERS_PAGE_CLEAN" }),
         onLoad: ({ orderBy }) => actionUsersPage({ orderBy }),
         onScroll: ({ feed, orderBy }) => actionFeedUsers({ skip: feed?.length || 0, orderBy }),
     }

+ 1 - 2
src/components/admin/AdminLayoutPage/AdminUserLayout/AdminUsersSearchPageContainer.js

@@ -2,7 +2,6 @@ import { connect } from "react-redux";
 import { useEffect } from "react";
 import { useSearchParams } from "react-router-dom";
 import { actionUsersSearchPage } from "../../../../actions/actionUsersSearchPage";
-import { actionUsersSearchPageClear } from "../../../../actions/actionUsersSearchPageClear";
 import { actionFeedUsersFind } from "../../../../reducers";
 import { InfScroll } from "../../../common/InfScroll";
 import { AdminUsersPage } from "../../AdminUsersPage";
@@ -37,7 +36,7 @@ export const CAdminUsersSearchPageContainer = connect(
         promiseStatus: state.promise?.feedUsersFind?.status || null,
     }),
     {
-        onUnmount: () => actionUsersSearchPageClear(),
+        onUnmount: () => ({ type: "USERS_SEARCH_PAGE_CLEAR" }),
         onLoad: ({ orderBy, text }) => actionUsersSearchPage({ orderBy, text }),
         onScroll: ({ feed, orderBy, text }) => actionFeedUsersFind({ text, skip: feed?.length || 0, orderBy }),
     }

+ 4 - 8
src/components/admin/AdminOrderPage/OrderForm.js

@@ -1,6 +1,5 @@
-import { connect, useDispatch, useSelector } from "react-redux";
+import { connect, useSelector } from "react-redux";
 import { useState, useEffect, useContext } from "react";
-import { actionPromiseClear } from "../../../reducers";
 import Select from "react-select";
 import { actionOrderUpdate } from "../../../actions/actionOrderUpdate";
 import { UIContext } from "../../UIContext";
@@ -11,6 +10,7 @@ import { OrderGoodsEditor } from "./OrderGoodsEditor";
 import { useNavigate } from "react-router-dom";
 import { actionOrderDelete } from "../../../actions/actionOrderDelete";
 import { ConfirmModal } from "../../common/ConfirmModal";
+import { actionPromisesClear } from "../../../actions/actionPromisesClear";
 
 export const OrderForm = ({
     serverErrors = [],
@@ -31,7 +31,6 @@ export const OrderForm = ({
     const [promiseTimeOut, setPromiseTimeOut] = useState(null);
     const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
     const navigate = useNavigate();
-    const dispatch = useDispatch();
 
     const formik = useFormik({
         initialValues: {},
@@ -73,7 +72,7 @@ export const OrderForm = ({
             });
         }
         if (promiseStatus === "REJECTED") {
-            const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
+            const errorMessage = (serverErrors ? [].concat(serverErrors) : []).reduce((prev, curr) => prev + "\n" + curr.message, "");
             formik.setSubmitting(false);
             promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);
@@ -104,9 +103,6 @@ export const OrderForm = ({
                 message: "Помилка",
             });
         }
-        return () => {
-            dispatch(actionPromiseClear("orderDelete"));
-        };
     }, [deletePromiseStatus]);
 
     useEffect(() => {
@@ -189,7 +185,7 @@ export const COrderForm = connect(
     }),
     {
         onSave: (order) => actionOrderUpdate(order),
-        onClose: () => actionPromiseClear("orderUpsert"),
+        onClose: () => actionPromisesClear(["orderUpsert", "orderDelete"]),
         onDelete: (order) => actionOrderDelete({ order }),
     }
 )(OrderForm);

+ 51 - 13
src/components/admin/AdminUserPage.js/UserForm.js

@@ -1,6 +1,5 @@
-import { connect, useDispatch } from "react-redux";
+import { connect } from "react-redux";
 import { useState, useEffect, useContext } from "react";
-import { actionPromiseClear } from "../../../reducers";
 import { actionUserUpdate } from "../../../actions/actionUserUpdate";
 import { UIContext } from "../../UIContext";
 import Select from "react-select";
@@ -12,6 +11,19 @@ import { MdVisibility, MdVisibilityOff } from "react-icons/md";
 import { aclList } from "../../../helpers";
 import { actionUploadFile } from "../../../actions/actionUploadFile";
 import { ProfileImageEditor } from "../../common/ProfileImageEditor";
+import { actionPromisesClear } from "../../../actions/actionPromisesClear";
+
+const styles = {
+    multiValue: (base, state) => {
+        return state.data.isFixed ? { ...base, backgroundColor: "gray" } : base;
+    },
+    multiValueLabel: (base, state) => {
+        return state.data.isFixed ? { ...base, fontWeight: "bold", color: "white", paddingRight: 6 } : base;
+    },
+    multiValueRemove: (base, state) => {
+        return state.data.isFixed ? { ...base, display: "none" } : base;
+    },
+};
 
 const CProfileImageEditor = connect(null, {
     onFileDrop: (acceptedFiles) => actionUploadFile(acceptedFiles[0]),
@@ -29,7 +41,7 @@ export const UserForm = ({
     onSaveClick,
     onSave,
     onClose,
-    onDelete,
+    onUnmount,
     promiseStatus,
     deletePromiseStatus,
     avatar = null,
@@ -41,7 +53,10 @@ export const UserForm = ({
 
     const [acl, setAcl] = useState([]);
     const navigate = useNavigate();
-    const dispatch = useDispatch();
+
+    useEffect(() => {
+        console.log(promiseStatus);
+    }, [promiseStatus]);
 
     const formik = useFormik({
         initialValues: {
@@ -56,15 +71,35 @@ export const UserForm = ({
             let userToSave = {};
             userToSave = formik.values;
             user?._id && (userToSave._id = user._id);
-            userToSave.acl = acl;
+            userToSave.acl = acl.map(({ value }) => value);
             avatar ? (userToSave.avatar = avatar) : delete userToSave.avatar;
-            console.log(userToSave);
             onSaveClick && onSaveClick();
             onSave(userToSave);
             setPromiseTimeOut(setTimeout(() => formik.setSubmitting(false), 3000));
         },
     });
 
+    const orderOptions = (values) => {
+        return values.filter((v) => v.isFixed).concat(values.filter((v) => !v.isFixed));
+    };
+
+    const onChange = (values, actionMeta) => {
+        switch (actionMeta.action) {
+            case "remove-value":
+            case "pop-value":
+                if (actionMeta.removedValue.isFixed) {
+                    return;
+                }
+                break;
+            case "clear":
+                values = aclList.filter((acl) => acl.isFixed);
+                break;
+        }
+
+        values = orderOptions(values);
+        setAcl(values);
+    };
+
     useEffect(() => {
         return () => {
             promiseTimeOut && clearTimeout(promiseTimeOut);
@@ -77,6 +112,7 @@ export const UserForm = ({
             formik.setSubmitting(false);
             promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);
+
             setAlert({
                 show: true,
                 severity: "success",
@@ -84,7 +120,7 @@ export const UserForm = ({
             });
         }
         if (promiseStatus === "REJECTED") {
-            const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
+            const errorMessage = (serverErrors ? [].concat(serverErrors) : []).reduce((prev, curr) => prev + "\n" + curr.message, "");
             formik.setSubmitting(false);
             promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);
@@ -112,12 +148,12 @@ export const UserForm = ({
             });
         }
         return () => {
-            dispatch(actionPromiseClear("userDelete"));
+            onUnmount && onUnmount();
         };
     }, [deletePromiseStatus]);
 
     useEffect(() => {
-        setAcl(user?.acl || []);
+        setAcl(orderOptions(aclList.filter((item) => user?.acl?.includes(item.value)) || []));
         formik.setFieldValue("name", user.name || "");
         formik.setFieldValue("username", user.username || "");
         formik.setFieldValue("nick", user.nick || "");
@@ -133,7 +169,7 @@ export const UserForm = ({
 
     return (
         <Box className="UserForm" component="form" onSubmit={formik.handleSubmit}>
-            <Grid container>
+            <Grid container spacing={2}>
                 <Grid item xs={5}>
                     <CProfileImageEditor avatar={avatar} />
                 </Grid>
@@ -212,11 +248,13 @@ export const UserForm = ({
                         <InputLabel>Permissions</InputLabel>
                         <Select
                             placeholder="Обрати категорії"
-                            value={acl.map((value) => ({ value, label: value }))}
+                            value={acl}
                             closeMenuOnSelect={false}
-                            onChange={(e) => setAcl(e.map(({ value }) => value))}
+                            onChange={onChange}
                             options={aclList}
+                            isClearable={acl?.some((acl) => !acl.isFixed)}
                             isMulti={true}
+                            styles={styles}
                         />
                     </Box>
                 </Grid>
@@ -241,6 +279,6 @@ export const CUserForm = connect(
     }),
     {
         onSave: (user) => actionUserUpdate(user),
-        onClose: () => actionPromiseClear("userUpsert"),
+        onClose: () => actionPromisesClear(["userUpsert", "userDelete"]),
     }
 )(UserForm);

+ 0 - 1
src/components/admin/AdminUsersPage/AdminUserList.js

@@ -18,7 +18,6 @@ const CSearchResults = connect((state) => ({ items: state.promise.adminUsersFind
 const AdminUserList = ({ users, orderBy = "_id" }) => {
     const [searchParams, setSearchParams] = useSearchParams();
     const navigate = useNavigate();
-    console.log(1);
     return (
         <Box className="AdminUserList">
             <Box className="searchBarWrapper">

+ 1 - 1
src/components/common/AuthModal/LoginForm.js

@@ -58,7 +58,7 @@ export const LoginForm = ({
             });
         }
         if (promiseStatus === "REJECTED") {
-            const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
+            const errorMessage = (serverErrors || []).reduce((prev, curr) => prev + "\n" + curr.message, "");
             formik.setSubmitting(false);
             promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);

+ 1 - 1
src/components/common/AuthModal/RegisterForm.js

@@ -56,7 +56,7 @@ export const RegisterForm = ({ serverErrors, promiseStatus, onRegister, onLoginB
             });
         }
         if (promiseStatus === "REJECTED") {
-            const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
+            const errorMessage = (serverErrors ? [].concat(serverErrors) : []).reduce((prev, curr) => prev + "\n" + curr.message, "");
             formik.setSubmitting(false);
             promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);

+ 8 - 6
src/components/common/DrawerCart/DrawerCart.js

@@ -1,16 +1,14 @@
 import { useNavigate } from "react-router-dom";
-import { actionCartDelete } from "../../../reducers";
 import { IoMdClose } from "react-icons/io";
 
 import { Divider, Typography, Button, Stack, IconButton } from "@mui/material";
-import { useSelector, useDispatch } from "react-redux";
 import { DrawerCartItem } from "./DrawerCartItem";
 import { DrawerRight } from "../DrawerRight";
 import { Box } from "@mui/system";
+import { connect } from "react-redux";
+import { actionCartDelete } from "../../../reducers";
 
-export const DrawerCart = ({ isOpen = false, onClose = null } = {}) => {
-    const cart = useSelector((state) => state.cart || {});
-    const dispatch = useDispatch();
+export const DrawerCart = ({ cart = {}, isOpen = false, onClose = null, onDeleteClick } = {}) => {
     const navigate = useNavigate();
 
     return (
@@ -28,7 +26,7 @@ export const DrawerCart = ({ isOpen = false, onClose = null } = {}) => {
 
                     <Divider />
                     {Object.entries(cart).map(([_id, order]) => (
-                        <DrawerCartItem order={order} onDeleteClick={(good) => dispatch(actionCartDelete(good))} key={_id} />
+                        <DrawerCartItem order={order} onDeleteClick={(good) => onDeleteClick(good)} key={_id} />
                     ))}
 
                     {!!Object.keys(cart).length && (
@@ -47,3 +45,7 @@ export const DrawerCart = ({ isOpen = false, onClose = null } = {}) => {
         </DrawerRight>
     );
 };
+
+export const CDrawerCart = connect((state) => ({ cart: state.cart || {} }), {
+    onDeleteClick: (good) => actionCartDelete(good),
+})(DrawerCart);

+ 3 - 15
src/components/common/SearchBar/SearchBar.js

@@ -20,7 +20,6 @@ export const SearchBar = ({
     const [searchParams] = useSearchParams();
     const [inputValue, setInputValue] = useState("");
     const [isChildrenOpen, setIsChildrenOpen] = useState(false);
-    const [inputTimeout, setInputTimeout] = useState(null);
     const [touched, setTouched] = useState(false);
     const R = render;
 
@@ -46,10 +45,6 @@ export const SearchBar = ({
     }, [searchParams]);
 
     useEffect(() => {
-        if (inputTimeout) {
-            clearTimeout(inputTimeout);
-        }
-
         const checkClickOutsideHeaderSearchBar = (e) => {
             if (ref.current && !ref.current.contains(e.target)) {
                 setIsChildrenOpen(false);
@@ -57,15 +52,8 @@ export const SearchBar = ({
                 inputValue.length && setIsChildrenOpen(true);
             }
         };
-        if (inputTimeout) {
-            clearTimeout(inputTimeout);
-            setInputTimeout(null);
-        }
-        setInputTimeout(
-            setTimeout(() => {
-                inputValue && onSearch(inputValue);
-            }, 700)
-        );
+
+        inputValue && onSearch(inputValue);
 
         touched && setIsChildrenOpen(!!inputValue?.length);
         document.addEventListener("mousedown", checkClickOutsideHeaderSearchBar);
@@ -121,6 +109,6 @@ export const SearchBar = ({
 };
 
 export const CSearchBar = connect(null, {
-    onSearch: (text) => actionGoodsFind({ text, limit: 5 }),
+    onSearch: (text) => actionGoodsFind({ text, limit: 5, delay: 1500 }),
     onSearchEnd: () => actionPromiseClear("goodsFind"),
 })(SearchBar);

+ 7 - 6
src/components/layout/Header/LogoutIcon/index.js

@@ -1,17 +1,18 @@
 import { Box, IconButton } from "@mui/material";
-import { useDispatch, useSelector } from "react-redux";
 import { MdLogout } from "react-icons/md";
 import { actionLogout } from "../../../../actions/actionLogout";
+import { connect } from "react-redux";
 
-export const LogoutIcon = () => {
-    const dispatch = useDispatch();
-    const token = useSelector((state) => state.auth?.token || null);
-
+export const LogoutIcon = ({ token, onClick }) => {
     return token ? (
         <Box className="LogoutIcon">
-            <IconButton onClick={() => dispatch(actionLogout())}>
+            <IconButton onClick={onClick}>
                 <MdLogout className="LogoutLogo" />
             </IconButton>
         </Box>
     ) : null;
 };
+
+export const CLogoutIcon = connect((state) => ({ token: state.auth?.token || null }), {
+    onClick: () => actionLogout(),
+})(LogoutIcon);

+ 5 - 6
src/components/layout/Header/index.js

@@ -1,22 +1,21 @@
 import { AppBar, Box, Button, IconButton, Stack, Toolbar, Typography } from "@mui/material";
 import { useState } from "react";
-import { useDispatch, useSelector } from "react-redux";
+import { useSelector } from "react-redux";
 import { createSearchParams, Link, useNavigate, useSearchParams } from "react-router-dom";
 import { ReactComponent as ShoppingLogo } from "../../../images/shopping-logo.svg";
 import { AuthModal } from "../../common/AuthModal";
-import { DrawerCart } from "../../common/DrawerCart/DrawerCart";
+import { CDrawerCart } from "../../common/DrawerCart/DrawerCart";
 import { CSearchBar } from "../../common/SearchBar";
 import { CSearchResults } from "../../common/SearchBar/SearchResults";
 import { AvatarButton } from "./AvatarButton";
 import { CCartIcon } from "./CartIcon";
-import { LogoutIcon } from "./LogoutIcon";
+import { CLogoutIcon } from "./LogoutIcon";
 
 const Header = () => {
     const rootCats = useSelector((state) => state?.promise?.rootCats?.payload || []);
     const [isCartDrawerOpen, setIsCartDrawerOpen] = useState(false);
     const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
     const navigate = useNavigate();
-    const dispatch = useDispatch();
     const [searchParams, setSearchParams] = useSearchParams();
     const token = useSelector((state) => state?.auth?.token || null);
 
@@ -54,7 +53,7 @@ const Header = () => {
                     <Stack direction="row" spacing={3}>
                         {token ? (
                             <Stack direction="row" spacing={3}>
-                                <LogoutIcon />
+                                <CLogoutIcon />
                                 <AvatarButton onClick={() => navigate("/dashboard/")} />
                             </Stack>
                         ) : (
@@ -72,7 +71,7 @@ const Header = () => {
                     </Stack>
                 </Toolbar>
             </AppBar>
-            <DrawerCart isOpen={isCartDrawerOpen} onClose={() => setIsCartDrawerOpen(false)} />
+            <CDrawerCart isOpen={isCartDrawerOpen} onClose={() => setIsCartDrawerOpen(false)} />
             <AuthModal open={isAuthModalOpen} onClose={() => setIsAuthModalOpen(false)} />
         </Box>
     );

+ 1 - 1
src/helpers/aclList.js

@@ -1,5 +1,5 @@
 export const aclList = [
     { label: "admin", value: "admin" },
     { label: "active", value: "active" },
-    // { label: "anon", value: "anon", isFixed: true, isVisited: true },
+    { label: "anon", value: "anon", isFixed: true },
 ];

+ 1 - 1
src/helpers/index.js

@@ -4,7 +4,7 @@ import { getGQL } from "./GraphQL";
 import { statusNumber, statusOptions } from "./orderStatus";
 import { aclList } from "./aclList";
 
-export const backendURL = "";
+export const backendURL = "http://188.72.209.29/api";
 export const gql = getGQL(backendURL + "/graphql/");
 
 export const mediaURL = "";

+ 0 - 4
src/images/defaultAvatarImage.png:Zone.Identifier

@@ -1,4 +0,0 @@
-[ZoneTransfer]
-ZoneId=3
-ReferrerUrl=https://www.google.com/
-HostUrl=https://avatarko.ru/img/kartinka/1/multfilm_gomer.png

+ 10 - 0
src/reducers/authReducer.js

@@ -1,3 +1,7 @@
+import { takeLatest } from "redux-saga/effects";
+import { loginWorker } from "../actions/actionLogin";
+import { logoutWorker } from "../actions/actionLogout";
+import { registerWorker } from "../actions/actionRegister";
 import { jwtDecode } from "../helpers";
 
 export function authReducer(state, { type, token }) {
@@ -31,4 +35,10 @@ export const actionAuthLogin = (token) => ({
     token: token,
 });
 
+export function* authWatcher() {
+    yield takeLatest("LOGIN", loginWorker);
+    yield takeLatest("LOGOUT", logoutWorker);
+    yield takeLatest("REGISTER", registerWorker);
+}
+
 export const actionAuthLogout = () => ({ type: "AUTH_LOGOUT" });

+ 15 - 45
src/reducers/feedReducer.js

@@ -24,59 +24,29 @@ function feedReducer(state = { payload: [] }, { type, payload = [] }) {
 
 const actionFeedAdd = (payload) => ({ type: "FEED_ADD", payload });
 const actionFeedClear = () => ({ type: "FEED_CLEAR" });
-const actionFeedGoods =
-    ({ skip = 0, orderBy = "_id" }) =>
-    async (dispatch, getState) => {
-        await dispatch(actionGoodsAll({ skip, limit: 1, promiseName: "feedGoodsAll", orderBy }));
-    };
+const actionFeedGoods = ({ skip = 0, orderBy = "_id" }) => actionGoodsAll({ skip, limit: 10, promiseName: "feedGoodsAll", orderBy });
 
-const actionFeedCategoryGoods =
-    ({ skip = 0, orderBy = "_id", category }) =>
-    async (dispatch, getState) => {
-        await dispatch(actionCategoryGoods({ skip, limit: 1, promiseName: "feedCategoryGoods", orderBy, category }));
-    };
+const actionFeedCategoryGoods = ({ skip = 0, orderBy = "_id", category }) =>
+    actionCategoryGoods({ skip, limit: 10, promiseName: "feedCategoryGoods", orderBy, category });
 
-const actionFeedGoodsFind =
-    ({ skip = 0, text = "", orderBy = "_id" }) =>
-    async (dispatch, getState) => {
-        await dispatch(actionGoodsFind({ skip, limit: 1, promiseName: "feedGoodsFind", text, orderBy }));
-    };
+const actionFeedGoodsFind = ({ skip = 0, text = "", orderBy = "_id" }) =>
+    actionGoodsFind({ skip, limit: 10, promiseName: "feedGoodsFind", text, orderBy });
 
-const actionFeedCatsFind =
-    ({ skip = 0, text = "", orderBy = "_id" }) =>
-    async (dispatch, getState) => {
-        await dispatch(actionCatsFind({ skip, promiseName: "feedCatsFind", text, limit: 1, orderBy }));
-    };
+const actionFeedCatsFind = ({ skip = 0, text = "", orderBy = "_id" }) =>
+    actionCatsFind({ skip, promiseName: "feedCatsFind", text, limit: 10, orderBy });
 
-const actionFeedCats =
-    ({ skip = 0, orderBy = "_id" }) =>
-    async (dispatch, getState) => {
-        await dispatch(actionCatAll({ promiseName: "feedCatAll", skip, limit: 1, orderBy }));
-    };
+const actionFeedCats = ({ skip = 0, orderBy = "_id" }) => actionCatAll({ promiseName: "feedCatAll", skip, limit: 10, orderBy });
 
-const actionFeedOrders =
-    ({ skip = 0, orderBy = "_id", status = 0 }) =>
-    async (dispatch, getState) => {
-        await dispatch(actionOrdersAll({ skip, limit: 1, promiseName: "feedOrdersAll", orderBy, status }));
-    };
+const actionFeedOrders = ({ skip = 0, orderBy = "_id", status = 0 }) =>
+    actionOrdersAll({ skip, limit: 10, promiseName: "feedOrdersAll", orderBy, status });
 
-const actionFeedOrdersFind =
-    ({ skip = 0, text = "", orderBy = "_id", status = "0" }) =>
-    async (dispatch, getState) => {
-        await dispatch(actionOrdersFind({ skip, limit: 1, promiseName: "feedOrdersFind", text, orderBy, status }));
-    };
+const actionFeedOrdersFind = ({ skip = 0, text = "", orderBy = "_id", status = "0" }) =>
+    actionOrdersFind({ skip, limit: 10, promiseName: "feedOrdersFind", text, orderBy, status });
 
-const actionFeedUsersFind =
-    ({ skip = 0, text = "", orderBy = "_id" }) =>
-    async (dispatch, getState) => {
-        await dispatch(actionUsersFind({ skip, promiseName: "feedUsersFind", text, limit: 1, orderBy }));
-    };
+const actionFeedUsersFind = ({ skip = 0, text = "", orderBy = "_id" }) =>
+    actionUsersFind({ skip, promiseName: "feedUsersFind", text, limit: 10, orderBy });
 
-const actionFeedUsers =
-    ({ skip = 0, orderBy = "_id" }) =>
-    async (dispatch, getState) => {
-        await dispatch(actionUsersAll({ promiseName: "feedUsersAll", skip, limit: 1, orderBy }));
-    };
+const actionFeedUsers = ({ skip = 0, orderBy = "_id" }) => actionUsersAll({ promiseName: "feedUsersAll", skip, limit: 10, orderBy });
 
 export {
     actionFeedCats,

+ 21 - 4
src/reducers/index.js

@@ -1,8 +1,17 @@
 import { createStore, combineReducers, applyMiddleware } from "redux";
-import { composeWithDevTools } from "redux-devtools-extension";
+import { all, put, takeEvery, takeLatest, takeLeading, select } from "redux-saga/effects";
 import thunk from "redux-thunk";
-import { authReducer, actionAuthLogin, actionAuthLogout } from "./authReducer";
-import { promiseReducer, actionPending, actionFulfilled, actionRejected, actionPromise, actionPromiseClear } from "./promiseReducer";
+import createSagaMiddleware from "redux-saga";
+import { authReducer, actionAuthLogin, actionAuthLogout, authWatcher } from "./authReducer";
+import {
+    promiseReducer,
+    actionPending,
+    actionFulfilled,
+    actionRejected,
+    actionPromise,
+    actionPromiseClear,
+    promiseWatcher,
+} from "./promiseReducer";
 import { cartReducer, actionCartAdd, actionCartChange, actionCartDelete, actionCartClear } from "./cartReducer";
 import {
     actionFeedCats,
@@ -34,6 +43,8 @@ export {
     actionFeedUsersFind,
     feedReducer,
 };
+
+const sagaMiddleware = createSagaMiddleware();
 export const store = createStore(
     combineReducers({
         auth: authReducer,
@@ -41,5 +52,11 @@ export const store = createStore(
         cart: cartReducer,
         feed: feedReducer,
     }),
-    composeWithDevTools(applyMiddleware(thunk))
+    applyMiddleware(sagaMiddleware)
 );
+
+function* rootSaga() {
+    yield all([promiseWatcher(), authWatcher()]);
+}
+
+sagaMiddleware.run(rootSaga);

+ 62 - 8
src/reducers/promiseReducer.js

@@ -1,3 +1,28 @@
+import { put, takeEvery, takeLatest } from "redux-saga/effects";
+import { aboutMeWorker } from "../actions/actionAboutMe";
+import { catByIdFullWorker } from "../actions/actionCatByIdFull";
+import { categoriesPageWorker } from "../actions/actionCategoriesPage";
+import { categoryGoodsWorker } from "../actions/actionCategoryGoods";
+import { categoryPageWorker } from "../actions/actionCategoryPage";
+import { categoryUpdateWorker } from "../actions/actionCategoryUpdate";
+import { goodPageWorker } from "../actions/actionGoodPage";
+import { goodsFindWorker } from "../actions/actionGoodsFind";
+import { goodsPageWorker } from "../actions/actionGoodsPage";
+import { goodsSearchPageWorker } from "../actions/actionGoodsSearchPage";
+import { goodUpdateWorker } from "../actions/actionGoodUpdate";
+import { orderPageWorker } from "../actions/actionOrderPage";
+import { ordersPageWorker } from "../actions/actionOrdersPage";
+import { ordersSearchPageWorker } from "../actions/actionOrdersSearchPage";
+import { orderUpdateWorker } from "../actions/actionOrderUpdate";
+import { pageStartWorker } from "../actions/actionPageStart";
+import { promisesClearWorker } from "../actions/actionPromisesClear";
+import { updateAvatarWorker } from "../actions/actionUpdateAvatar";
+import { uploadFilesWorker } from "../actions/actionUploadFiles";
+import { userPageWorker } from "../actions/actionUserPage";
+import { usersPageWorker } from "../actions/actionUsersPage";
+import { usersSearchPageWorker } from "../actions/actionUsersSearchPage";
+import { userUpdateWorker } from "../actions/actionUserUpdate";
+
 export function promiseReducer(state = {}, { type, name, status, payload, error }) {
     if (type === "PROMISE") {
         return {
@@ -16,15 +41,44 @@ export const actionPending = (name) => ({ type: "PROMISE", name, status: "PENDIN
 export const actionFulfilled = (name, payload) => ({ type: "PROMISE", name, status: "FULFILLED", payload });
 export const actionRejected = (name, error) => ({ type: "PROMISE", name, status: "REJECTED", error });
 export const actionPromiseClear = (name) => ({ type: "PROMISE_CLEAR", name });
-export const actionPromise = (name, promise) => async (dispatch) => {
-    dispatch(actionPending(name));
 
-    try {
-        let payload = await promise;
-        dispatch(actionFulfilled(name, payload));
+export const actionPromise = (name, promise) => ({ type: "PROMISE_START", name, promise });
 
-        return payload;
+export function* promiseWorker(action) {
+    const { name, promise } = action;
+    yield put(actionPending(name));
+    try {
+        let data = yield promise;
+        yield put(actionFulfilled(name, data));
+        return data;
     } catch (error) {
-        dispatch(actionRejected(name, JSON.parse(error.message)));
+        yield put(actionRejected(name, error));
     }
-};
+}
+
+export function* promiseWatcher() {
+    yield takeEvery("PROMISE_START", promiseWorker);
+    yield takeLatest("PAGE_START", pageStartWorker);
+    yield takeLatest("ABOUT_ME", aboutMeWorker);
+    yield takeLatest("CAT_BY_ID_FULL", catByIdFullWorker);
+    yield takeLatest("CATEGORY_GOODS", categoryGoodsWorker);
+    yield takeLatest("ORDER_UPDATE", orderUpdateWorker);
+    yield takeLatest("GOODS_FIND", goodsFindWorker);
+    yield takeLatest("GOOD_PAGE", goodPageWorker);
+    yield takeLatest("GOODS_SEARCH_PAGE", goodsSearchPageWorker);
+    yield takeLatest("CATEGORIES_PAGE", categoriesPageWorker);
+    yield takeLatest("GOODS_PAGE", goodsPageWorker);
+    yield takeLatest("CATEGORY_PAGE", categoryPageWorker);
+    yield takeLatest("CATEGORY_UPDATE", categoryUpdateWorker);
+    yield takeLatest("GOOD_UPDATE", goodUpdateWorker);
+    yield takeLatest("ORDER_PAGE", orderPageWorker);
+    yield takeLatest("ORDERS_PAGE", ordersPageWorker);
+    yield takeLatest("ORDERS_SEARCH_PAGE", ordersSearchPageWorker);
+    yield takeLatest("UPDATE_AVATAR", updateAvatarWorker);
+    yield takeLatest("UPLOAD_FILES", uploadFilesWorker);
+    yield takeLatest("USER_PAGE", userPageWorker);
+    yield takeLatest("USERS_PAGE", usersPageWorker);
+    yield takeLatest("USERS_SEARCH_PAGE", usersSearchPageWorker);
+    yield takeLatest("USER_UPDATE", userUpdateWorker);
+    yield takeEvery("PROMISES_CLEAR", promisesClearWorker);
+}

+ 77 - 0
yarn.lock

@@ -1033,6 +1033,13 @@
   dependencies:
     regenerator-runtime "^0.13.4"
 
+"@babel/runtime@^7.6.3":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.6.tgz#6a1ef59f838debd670421f8c7f2cbb8da9751580"
+  integrity sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
 "@babel/template@^7.16.7", "@babel/template@^7.3.3":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
@@ -1756,6 +1763,50 @@
   resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz#a3031eb54129f2c66b2753f8404266ec7bf67f0a"
   integrity sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==
 
+"@redux-saga/core@^1.1.3":
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/@redux-saga/core/-/core-1.1.3.tgz#3085097b57a4ea8db5528d58673f20ce0950f6a4"
+  integrity sha512-8tInBftak8TPzE6X13ABmEtRJGjtK17w7VUs7qV17S8hCO5S3+aUTWZ/DBsBJPdE8Z5jOPwYALyvofgq1Ws+kg==
+  dependencies:
+    "@babel/runtime" "^7.6.3"
+    "@redux-saga/deferred" "^1.1.2"
+    "@redux-saga/delay-p" "^1.1.2"
+    "@redux-saga/is" "^1.1.2"
+    "@redux-saga/symbols" "^1.1.2"
+    "@redux-saga/types" "^1.1.0"
+    redux "^4.0.4"
+    typescript-tuple "^2.2.1"
+
+"@redux-saga/deferred@^1.1.2":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@redux-saga/deferred/-/deferred-1.1.2.tgz#59937a0eba71fff289f1310233bc518117a71888"
+  integrity sha512-908rDLHFN2UUzt2jb4uOzj6afpjgJe3MjICaUNO3bvkV/kN/cNeI9PMr8BsFXB/MR8WTAZQq/PlTq8Kww3TBSQ==
+
+"@redux-saga/delay-p@^1.1.2":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@redux-saga/delay-p/-/delay-p-1.1.2.tgz#8f515f4b009b05b02a37a7c3d0ca9ddc157bb355"
+  integrity sha512-ojc+1IoC6OP65Ts5+ZHbEYdrohmIw1j9P7HS9MOJezqMYtCDgpkoqB5enAAZrNtnbSL6gVCWPHaoaTY5KeO0/g==
+  dependencies:
+    "@redux-saga/symbols" "^1.1.2"
+
+"@redux-saga/is@^1.1.2":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@redux-saga/is/-/is-1.1.2.tgz#ae6c8421f58fcba80faf7cadb7d65b303b97e58e"
+  integrity sha512-OLbunKVsCVNTKEf2cH4TYyNbbPgvmZ52iaxBD4I1fTif4+MTXMa4/Z07L83zW/hTCXwpSZvXogqMqLfex2Tg6w==
+  dependencies:
+    "@redux-saga/symbols" "^1.1.2"
+    "@redux-saga/types" "^1.1.0"
+
+"@redux-saga/symbols@^1.1.2":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@redux-saga/symbols/-/symbols-1.1.2.tgz#216a672a487fc256872b8034835afc22a2d0595d"
+  integrity sha512-EfdGnF423glv3uMwLsGAtE6bg+R9MdqlHEzExnfagXPrIiuxwr3bdiAwz3gi+PsrQ3yBlaBpfGLtDG8rf3LgQQ==
+
+"@redux-saga/types@^1.1.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@redux-saga/types/-/types-1.1.0.tgz#0e81ce56b4883b4b2a3001ebe1ab298b84237204"
+  integrity sha512-afmTuJrylUU/0OtqzaRkbyYFFNgCF73Bvel/sw90pvGrWIZ+vyoIJqA6eMSoA6+nb443kTmulmBtC9NerXboNg==
+
 "@rollup/plugin-babel@^5.2.0":
   version "5.3.1"
   resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"
@@ -8618,6 +8669,13 @@ redux-devtools-extension@^2.13.9:
   resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz#6b764e8028b507adcb75a1cae790f71e6be08ae7"
   integrity sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==
 
+redux-saga@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-1.1.3.tgz#9f3e6aebd3c994bbc0f6901a625f9a42b51d1112"
+  integrity sha512-RkSn/z0mwaSa5/xH/hQLo8gNf4tlvT18qXDNvedihLcfzh+jMchDgaariQoehCpgRltEm4zHKJyINEz6aqswTw==
+  dependencies:
+    "@redux-saga/core" "^1.1.3"
+
 redux-thunk@^2.4.1:
   version "2.4.1"
   resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714"
@@ -9841,6 +9899,25 @@ typedarray-to-buffer@^3.1.5:
   dependencies:
     is-typedarray "^1.0.0"
 
+typescript-compare@^0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/typescript-compare/-/typescript-compare-0.0.2.tgz#7ee40a400a406c2ea0a7e551efd3309021d5f425"
+  integrity sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==
+  dependencies:
+    typescript-logic "^0.0.0"
+
+typescript-logic@^0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/typescript-logic/-/typescript-logic-0.0.0.tgz#66ebd82a2548f2b444a43667bec120b496890196"
+  integrity sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==
+
+typescript-tuple@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/typescript-tuple/-/typescript-tuple-2.2.1.tgz#7d9813fb4b355f69ac55032e0363e8bb0f04dad2"
+  integrity sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==
+  dependencies:
+    typescript-compare "^0.0.2"
+
 unbox-primitive@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"