2 Commits 91be33a42d ... 5e510d1c0c

Auteur SHA1 Bericht Datum
  ilya_shyian 5e510d1c0c + final build (i hope) 2 jaren geleden
  ilya_shyian cfbc74ca6c beafor final build 2 jaren geleden
48 gewijzigde bestanden met toevoegingen van 408 en 87 verwijderingen
  1. 19 0
      build/asset-manifest.json
  2. 1 0
      build/index.html
  3. 2 0
      build/static/css/main.928beea0.css
  4. 1 0
      build/static/css/main.928beea0.css.map
  5. 2 0
      build/static/js/787.884e199e.chunk.js
  6. 1 0
      build/static/js/787.884e199e.chunk.js.map
  7. 3 0
      build/static/js/main.a7c740d9.js
  8. 156 0
      build/static/js/main.a7c740d9.js.LICENSE.txt
  9. 1 0
      build/static/js/main.a7c740d9.js.map
  10. 1 0
      build/static/media/404.7e060e4273fbd7fc09f0a17ca7f3dd68.svg
  11. BIN
      build/static/media/default-avatar-image.fb653606e5a348e7bc76.png
  12. BIN
      build/static/media/main-page-image.bc655052ce386c031a15.png
  13. 7 0
      build/static/media/shopping-logo.423022a24e67b6b6140c294be23af1b0.svg
  14. 1 1
      src/actions/actionCatById.js
  15. 1 1
      src/actions/actionCategoryGoods.js
  16. 2 2
      src/actions/actionCatsFind.js
  17. 1 1
      src/actions/actionGoodPage.js
  18. 2 3
      src/actions/actionGoodsFind.js
  19. 11 8
      src/actions/actionLogin.js
  20. 1 1
      src/actions/actionOrderById.js
  21. 1 1
      src/actions/actionOrderUpsert.js
  22. 2 2
      src/actions/actionOrdersFind.js
  23. 7 2
      src/actions/actionUpdateAvatar.js
  24. 1 1
      src/actions/actionUploadFiles.js
  25. 1 1
      src/actions/actionUserById.js
  26. 12 1
      src/actions/actionUserUpdate.js
  27. 1 2
      src/actions/actionUsersFind.js
  28. 6 10
      src/components/DashboardPage/ProfileForm/index.js
  29. 0 1
      src/components/DashboardPage/index.js
  30. 8 3
      src/components/GoodsPage/index.js
  31. 15 5
      src/components/admin/AdminCategoriesPage/AdminCategoryList.js
  32. 0 1
      src/components/admin/AdminGoodPage/GoodForm.js
  33. 14 5
      src/components/admin/AdminGoodsPage/AdminGoodList.js
  34. 1 1
      src/components/admin/AdminLayoutPage/AdminOrderLayout/AdminOrdersSearchPageContainer.js
  35. 13 3
      src/components/admin/AdminOrdersPage/AdminOrderList.js
  36. 13 3
      src/components/admin/AdminUsersPage/AdminUserList.js
  37. 7 1
      src/components/common/AuthModal/LoginForm.js
  38. 1 2
      src/components/common/AuthModal/RegisterForm.js
  39. 1 1
      src/components/common/DropZone/index.js
  40. 0 1
      src/components/common/GoodList/index.js
  41. 1 0
      src/components/common/SearchBar/SearchCategoryResultItem.js
  42. 1 1
      src/components/common/SearchBar/SearchUserResultItem.js
  43. 0 3
      src/components/common/SearchBar/index.js
  44. 1 1
      src/components/layout/Aside/index.js
  45. 9 0
      src/index.scss
  46. 74 15
      src/reducers/feedReducer.js
  47. 3 3
      src/reducers/index.js
  48. 2 0
      src/reducers/promiseReducer.js

+ 19 - 0
build/asset-manifest.json

@@ -0,0 +1,19 @@
+{
+  "files": {
+    "main.css": "/static/css/main.928beea0.css",
+    "main.js": "/static/js/main.a7c740d9.js",
+    "static/js/787.884e199e.chunk.js": "/static/js/787.884e199e.chunk.js",
+    "static/media/main-page-image.png": "/static/media/main-page-image.bc655052ce386c031a15.png",
+    "static/media/default-avatar-image.png": "/static/media/default-avatar-image.fb653606e5a348e7bc76.png",
+    "static/media/404.svg": "/static/media/404.7e060e4273fbd7fc09f0a17ca7f3dd68.svg",
+    "static/media/shopping-logo.svg": "/static/media/shopping-logo.423022a24e67b6b6140c294be23af1b0.svg",
+    "index.html": "/index.html",
+    "main.928beea0.css.map": "/static/css/main.928beea0.css.map",
+    "main.a7c740d9.js.map": "/static/js/main.a7c740d9.js.map",
+    "787.884e199e.chunk.js.map": "/static/js/787.884e199e.chunk.js.map"
+  },
+  "entrypoints": [
+    "static/css/main.928beea0.css",
+    "static/js/main.a7c740d9.js"
+  ]
+}

File diff suppressed because it is too large
+ 1 - 0
build/index.html


File diff suppressed because it is too large
+ 2 - 0
build/static/css/main.928beea0.css


File diff suppressed because it is too large
+ 1 - 0
build/static/css/main.928beea0.css.map


File diff suppressed because it is too large
+ 2 - 0
build/static/js/787.884e199e.chunk.js


File diff suppressed because it is too large
+ 1 - 0
build/static/js/787.884e199e.chunk.js.map


File diff suppressed because it is too large
+ 3 - 0
build/static/js/main.a7c740d9.js


+ 156 - 0
build/static/js/main.a7c740d9.js.LICENSE.txt

@@ -0,0 +1,156 @@
+/*
+object-assign
+(c) Sindre Sorhus
+@license MIT
+*/
+
+/*!
+  Copyright (c) 2018 Jed Watson.
+  Licensed under the MIT License (MIT), see
+  http://jedwatson.github.io/classnames
+*/
+
+/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
+
+/**
+ * @license React
+ * react-dom.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * @license React
+ * react-is.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * @license React
+ * react-jsx-runtime.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * @license React
+ * react.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * @license React
+ * scheduler.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * @license React
+ * use-sync-external-store-shim.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * @license React
+ * use-sync-external-store-shim/with-selector.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * React Router DOM v6.3.0
+ *
+ * Copyright (c) Remix Software Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE.md file in the root directory of this source tree.
+ *
+ * @license MIT
+ */
+
+/**
+ * React Router v6.3.0
+ *
+ * Copyright (c) Remix Software Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE.md file in the root directory of this source tree.
+ *
+ * @license MIT
+ */
+
+/** @license MUI v5.8.0
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/** @license React v0.19.1
+ * scheduler.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/** @license React v16.13.1
+ * react-is.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/** @license React v16.14.0
+ * react-dom.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/** @license React v16.14.0
+ * react.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/** @license React v17.0.2
+ * react-is.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */

File diff suppressed because it is too large
+ 1 - 0
build/static/js/main.a7c740d9.js.map


File diff suppressed because it is too large
+ 1 - 0
build/static/media/404.7e060e4273fbd7fc09f0a17ca7f3dd68.svg


BIN
build/static/media/default-avatar-image.fb653606e5a348e7bc76.png


BIN
build/static/media/main-page-image.bc655052ce386c031a15.png


File diff suppressed because it is too large
+ 7 - 0
build/static/media/shopping-logo.423022a24e67b6b6140c294be23af1b0.svg


+ 1 - 1
src/actions/actionCatById.js

@@ -2,7 +2,7 @@ import { gql } from "../helpers";
 
 import { actionPromise } from "../reducers";
 
-export const actionCatById = ({ _id, promiseName = "catById", orderBy = "", limit = 20, skip = 0 }) =>
+export const actionCatById = ({ _id, promiseName = "catById", orderBy = "", limit = 20, skip = 0 } = {}) =>
     actionPromise(
         promiseName,
         gql(

+ 1 - 1
src/actions/actionCategoryGoods.js

@@ -1,4 +1,4 @@
-import { call, cancelled, select } from "redux-saga/effects";
+import { call } from "redux-saga/effects";
 import { gql } from "../helpers";
 import { actionPromise } from "../reducers";
 import { promiseWorker } from "../reducers/promiseReducer";

+ 2 - 2
src/actions/actionCatsFind.js

@@ -1,7 +1,7 @@
 import { gql } from "../helpers";
 import { actionPromise } from "../reducers";
 
-export const actionCatsFind = ({ text = "", limit = 7, skip = 0, promiseName = "catsFind", orderBy = "_id" }) =>
+export const actionCatsFind = ({ text = "", limit = 7, skip = 0, promiseName = "catsFind", orderBy = "_id" } = {}) =>
     actionPromise(
         promiseName,
         gql(
@@ -16,7 +16,7 @@ export const actionCatsFind = ({ text = "", limit = 7, skip = 0, promiseName = "
                     }`,
             {
                 query: JSON.stringify([
-                    { name__icontains: text, _id__icontains: text },
+                    { or: { name__icontains: text, _id__icontains: text } },
                     {
                         limit: !!limit ? limit : 5,
                         skip,

+ 1 - 1
src/actions/actionGoodPage.js

@@ -10,7 +10,7 @@ export const actionGoodPage = ({ _id, promiseName } = {}) => ({
 });
 
 export function* goodPageWorker(action) {
-    const { _id, promiseName = "goodById" } = action.payload;
+    const { _id, promiseName = "goodById" } = action.payload || {};
     yield put(actionCatAll());
 
     if (_id) {

+ 2 - 3
src/actions/actionGoodsFind.js

@@ -1,4 +1,4 @@
-import { call, delay, put } from "redux-saga/effects";
+import { call, delay } from "redux-saga/effects";
 import { gql } from "../helpers";
 import { actionPromise } from "../reducers";
 import { promiseWorker } from "../reducers/promiseReducer";
@@ -28,8 +28,7 @@ export function* goodsFindWorker(action) {
                 {
                     query: JSON.stringify([
                         {
-                            name__icontains: text,
-                            description__icontains: text,
+                            or: { name__icontains: text, description__icontains: text },
                         },
                         {
                             limit: !!limit ? limit : 5,

+ 11 - 8
src/actions/actionLogin.js

@@ -2,14 +2,15 @@ import { actionPromise } from "../reducers";
 import { gql } from "../helpers";
 import { actionAuthLogin } from "../reducers";
 import { actionAboutMe } from "./actionAboutMe";
-import { actionLogout } from "./actionLogout";
+import { actionLogout, logoutWorker } from "./actionLogout";
 import { promiseWorker } from "../reducers/promiseReducer";
 import { call, put } from "redux-saga/effects";
 
 export const actionLogin = (username, password) => ({ type: "LOGIN", payload: { username, password } });
 export function* loginWorker(action) {
     const { username, password } = action.payload || {};
-    yield call(promiseWorker, actionLogout());
+    yield call(logoutWorker, actionLogout());
+
     const token = yield call(
         promiseWorker,
         actionPromise(
@@ -25,11 +26,13 @@ export function* loginWorker(action) {
         )
     );
 
-    if (typeof token === "string") {
-        yield put(actionAuthLogin(token));
-    } else {
-        yield put(actionAuthLogin(token.token));
-    }
+    if (token) {
+        if (typeof token === "string") {
+            yield put(actionAuthLogin(token));
+        } else {
+            yield put(actionAuthLogin(token.token));
+        }
 
-    yield put(actionAboutMe());
+        yield put(actionAboutMe());
+    }
 }

+ 1 - 1
src/actions/actionOrderById.js

@@ -2,7 +2,7 @@ import { gql } from "../helpers";
 
 import { actionPromise } from "../reducers";
 
-export const actionOrderById = ({ _id, promiseName = "orderById" }) =>
+export const actionOrderById = ({ _id, promiseName = "orderById" } = {}) =>
     actionPromise(
         promiseName,
         gql(

+ 1 - 1
src/actions/actionOrderUpsert.js

@@ -1,5 +1,5 @@
 import { gql } from "../helpers";
-import { actionCartClear, actionPromise } from "../reducers";
+import { actionPromise } from "../reducers";
 
 export const actionOrderUpsert = (order) =>
     actionPromise(

+ 2 - 2
src/actions/actionOrdersFind.js

@@ -1,7 +1,7 @@
 import { gql } from "../helpers";
 import { actionPromise } from "../reducers";
 
-export const actionOrdersFind = ({ text = "", limit = 7, skip = 0, promiseName = "ordersFind", orderBy = "_id", status = "0" }) =>
+export const actionOrdersFind = ({ text = "", limit = 7, skip = 0, promiseName = "ordersFind", orderBy = "_id", status = "0" } = {}) =>
     actionPromise(
         promiseName,
         gql(
@@ -15,7 +15,7 @@ export const actionOrdersFind = ({ text = "", limit = 7, skip = 0, promiseName =
                     }`,
             {
                 query: JSON.stringify([
-                    { owner__username__icontains: text, _id__icontains: text, status__icontains: text, status },
+                    { or: { owner__username__icontains: text, _id__icontains: text, status__icontains: text }, status },
                     {
                         limit: !!limit ? limit : 5,
                         skip,

+ 7 - 2
src/actions/actionUpdateAvatar.js

@@ -14,13 +14,18 @@ export function* updateAvatarWorker(action) {
     const {
         promise: {
             uploadFile: {
-                payload: { _id },
+                payload: { _id: fileId },
                 status,
             },
         },
+        auth: {
+            payload: {
+                sub: { _id },
+            },
+        },
     } = yield select();
 
-    yield call(promiseWorker, actionUserUpsert({ avatar: { _id } }));
+    yield call(promiseWorker, actionUserUpsert({ avatar: { _id: fileId }, _id }));
 
     if (status === "FULFILLED") {
         yield put(actionAboutMe());

+ 1 - 1
src/actions/actionUploadFiles.js

@@ -1,6 +1,6 @@
 import { actionUploadFile } from "./actionUploadFile";
 import { actionPromise } from "../reducers";
-import { all, call, put } from "redux-saga/effects";
+import { all, call } from "redux-saga/effects";
 import { promiseWorker } from "../reducers/promiseReducer";
 
 export const actionUploadFiles = (files = []) => ({ type: "UPLOAD_FILES", payload: files });

+ 1 - 1
src/actions/actionUserById.js

@@ -2,7 +2,7 @@ import { gql } from "../helpers";
 
 import { actionPromise } from "../reducers";
 
-export const actionUserById = ({ _id, promiseName = "adminUserById" }) =>
+export const actionUserById = ({ _id, promiseName = "adminUserById" } = {}) =>
     actionPromise(
         promiseName,
         gql(

+ 12 - 1
src/actions/actionUserUpdate.js

@@ -2,6 +2,7 @@ import { call, delay, put, select } from "redux-saga/effects";
 import { actionPromiseClear } from "../reducers";
 import { promiseWorker } from "../reducers/promiseReducer";
 import { actionAboutMe } from "./actionAboutMe";
+import { actionLogout } from "./actionLogout";
 import { actionUserUpsert } from "./actionUserUpsert";
 
 export const actionUserUpdate = (user) => ({ type: "USER_UPDATE", payload: user });
@@ -18,10 +19,20 @@ export function* userUpdateWorker(action) {
         promise: {
             userUpsert: { status },
         },
+        auth: {
+            payload: {
+                sub: { acl },
+                username,
+            },
+        },
     } = yield select();
 
     if (status === "FULFILLED") {
-        yield put(actionAboutMe());
+        if (!acl?.includes("admin") && user?.username !== username) {
+            yield put(actionLogout());
+        } else {
+            yield put(actionAboutMe());
+        }
     }
 
     yield delay(500);

+ 1 - 2
src/actions/actionUsersFind.js

@@ -16,8 +16,7 @@ export const actionUsersFind = ({ text = "", limit = 0, skip = 0, promiseName =
             {
                 query: JSON.stringify([
                     {
-                        username__icontains: text,
-                        _id__icontains: text,
+                        or: { username__icontains: text, _id__icontains: text },
                     },
                     {
                         limit: !!limit ? limit : 100,

+ 6 - 10
src/components/DashboardPage/ProfileForm/index.js

@@ -1,10 +1,9 @@
-import { Box, Button, Grid, IconButton, Paper, Table, TableBody, TableCell, TableRow, TextField } from "@mui/material";
+import { Box, Button, Grid, IconButton, Table, TableBody, TableCell, TableRow, TextField } from "@mui/material";
 import { useContext, useEffect, useState } from "react";
 
 import { connect } from "react-redux";
 import { actionUpdateAvatar } from "../../../actions/actionUpdateAvatar";
 import { actionUserUpdate } from "../../../actions/actionUserUpdate";
-import { DropZone } from "../../common/DropZone";
 import { useFormik } from "formik";
 import * as Yup from "yup";
 import { UIContext } from "../../UIContext";
@@ -38,7 +37,6 @@ export const ProfileForm = ({ profile = {}, promiseStatus, onProfileSave, server
         validationSchema: profileSchema,
         validateOnChange: true,
         onSubmit: () => {
-            console.log(formik.values);
             onProfileSave(formik.values);
             setPromiseTimeOut(setTimeout(() => formik.setSubmitting(false), 3000));
         },
@@ -52,14 +50,10 @@ export const ProfileForm = ({ profile = {}, promiseStatus, onProfileSave, server
     }, []);
 
     useEffect(() => {
+        profile._id && formik.setFieldValue("_id", profile?._id || "");
         formik.setFieldValue("username", profile?.username || "");
         formik.setFieldValue("nick", profile?.nick || "");
         formik.setFieldValue("name", profile?.name || "");
-
-        return () => {
-            promiseTimeOut && clearTimeout(promiseTimeOut);
-            setPromiseTimeOut(null);
-        };
     }, [profile]);
 
     useEffect(() => {
@@ -110,7 +104,9 @@ export const ProfileForm = ({ profile = {}, promiseStatus, onProfileSave, server
                                             value={formik.values.username}
                                             onBlur={formik.handleBlur}
                                             onChange={formik.handleChange}
-                                            helperText={formik.touched.username && formik.errors.username}
+                                            helperText={`Після зміни username потрібно перезайти в аккаунт!!! \n ${
+                                                formik.touched.username ? formik.errors.username || "" : ""
+                                            }`}
                                         />
                                     ) : (
                                         formik.values.username
@@ -222,7 +218,7 @@ export const ProfileForm = ({ profile = {}, promiseStatus, onProfileSave, server
 
 export const CProfileForm = connect(
     (state) => ({
-        profile: state.promise?.aboutMe?.payload,
+        profile: state.promise?.aboutMe?.payload || {},
         promiseStatus: state.promise.userUpsert?.status || null,
         serverErrors: state.promise?.userUpsert?.error || [],
     }),

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

@@ -1,6 +1,5 @@
 import { Box, Paper, Stack, Typography } from "@mui/material";
 import { connect } from "react-redux";
-import { Error } from "../common/Error";
 import { DashboardOrder } from "./DashboardOrder";
 import { CProfileForm } from "./ProfileForm";
 

+ 8 - 3
src/components/GoodsPage/index.js

@@ -1,4 +1,4 @@
-import { Stack, Typography, Divider } from "@mui/material";
+import { Stack, Typography, Divider, LinearProgress } from "@mui/material";
 import { Box } from "@mui/system";
 import { connect } from "react-redux";
 import { useSearchParams } from "react-router-dom";
@@ -6,7 +6,7 @@ import { GoodList } from "../common/GoodList";
 import { SubCategories } from "./SubCategories";
 import { SortOptions } from "../common/SortOptions";
 
-const GoodsPage = ({ category = {}, goods = [] }) => {
+const GoodsPage = ({ category = {}, goods = [], promiseStatus = null }) => {
     const { name = "", subcategories = [] } = category || {};
     const [searchParams, setSearchParams] = useSearchParams();
 
@@ -45,10 +45,15 @@ const GoodsPage = ({ category = {}, goods = [] }) => {
                         <GoodList goods={goods} />
                     </Box>
                 ) : null}
+                {promiseStatus === "PENDING" && <LinearProgress />}
             </Stack>
         </Box>
     );
 };
 
-const CGoodsPage = connect((state) => ({ category: state?.promise?.catById?.payload || {}, goods: state?.feed?.payload || [] }))(GoodsPage);
+const CGoodsPage = connect((state) => ({
+    category: state?.promise?.catById?.payload || {},
+    goods: state?.feed?.payload || [],
+    promiseStatus: state.promise?.feedCategoryGoods?.status || state.promise?.feedGoodsFind?.status || null,
+}))(GoodsPage);
 export { GoodsPage, CGoodsPage };

+ 15 - 5
src/components/admin/AdminCategoriesPage/AdminCategoryList.js

@@ -4,8 +4,8 @@ import { connect } from "react-redux";
 import { actionCatsFind } from "../../../actions/actionCatsFind";
 import { actionPromiseClear } from "../../../reducers";
 import { SearchBar, SearchResults } from "../../common/SearchBar";
-import { Box, Table, TableBody, TableHead } from "@mui/material";
-import { createSearchParams, useLocation, useNavigate, useSearchParams } from "react-router-dom";
+import { Box, LinearProgress, Table, TableBody, TableCell, TableHead, TableRow } from "@mui/material";
+import { createSearchParams, useNavigate, useSearchParams } from "react-router-dom";
 
 const CSearchBar = connect(null, {
     onSearch: (text) => actionCatsFind({ promiseName: "adminCatsFind", text, limit: 5 }),
@@ -14,9 +14,9 @@ const CSearchBar = connect(null, {
 
 const CSearchResults = connect((state) => ({ items: state.promise.adminCatsFind?.payload || [] }))(SearchResults);
 
-const AdminCategoryList = ({ categories, orderBy = "_id" } = {}) => {
+const AdminCategoryList = ({ categories, orderBy = "_id", promiseStatus = null } = {}) => {
     const navigate = useNavigate();
-    const location = useLocation();
+
     const [searchParams, setSearchParams] = useSearchParams();
 
     return (
@@ -47,12 +47,22 @@ const AdminCategoryList = ({ categories, orderBy = "_id" } = {}) => {
                     {(categories || []).map((cat) => (
                         <AdminCategoryItem category={cat} key={cat._id} />
                     ))}
+                    {promiseStatus === "PENDING" && (
+                        <TableRow>
+                            <TableCell colSpan="4">
+                                <LinearProgress />
+                            </TableCell>
+                        </TableRow>
+                    )}
                 </TableBody>
             </Table>
         </Box>
     );
 };
 
-const CAdminCategoryList = connect((state) => ({ categories: state.feed?.payload || [] }))(AdminCategoryList);
+const CAdminCategoryList = connect((state) => ({
+    promiseStatus: state?.promise?.feedCatAll?.status || state?.promise?.feedCatsFind?.status || null,
+    categories: state.feed?.payload || [],
+}))(AdminCategoryList);
 
 export { AdminCategoryList, CAdminCategoryList };

+ 0 - 1
src/components/admin/AdminGoodPage/GoodForm.js

@@ -93,7 +93,6 @@ export const GoodForm = ({
             });
         }
         if (promiseStatus === "REJECTED") {
-            console.log(serverErrors);
             const errorMessage = (serverErrors ? [].concat(serverErrors) : []).reduce((prev, curr) => prev + "\n" + curr.message, "");
             formik.setSubmitting(false);
             promiseTimeOut && clearTimeout(promiseTimeOut);

+ 14 - 5
src/components/admin/AdminGoodsPage/AdminGoodList.js

@@ -5,8 +5,8 @@ import { connect } from "react-redux";
 import { SearchBar, SearchResults } from "../../common/SearchBar";
 import { actionGoodsFind } from "../../../actions/actionGoodsFind";
 import { actionPromiseClear } from "../../../reducers";
-import { Box, Table, TableBody, TableHead } from "@mui/material";
-import { createSearchParams, useLocation, useNavigate, useSearchParams } from "react-router-dom";
+import { Box, LinearProgress, Table, TableBody, TableCell, TableHead, TableRow } from "@mui/material";
+import { createSearchParams, useNavigate, useSearchParams } from "react-router-dom";
 
 const CSearchBar = connect(null, {
     onSearch: (text) => actionGoodsFind({ promiseName: "adminGoodsFind", text, limit: 5 }),
@@ -15,9 +15,8 @@ const CSearchBar = connect(null, {
 
 const CSearchResults = connect((state) => ({ items: state.promise.adminGoodsFind?.payload || [] }))(SearchResults);
 
-const AdminGoodList = ({ goods, orderBy = "_id" }) => {
+const AdminGoodList = ({ goods, orderBy = "_id", promiseStatus = null } = {}) => {
     const navigate = useNavigate();
-    const location = useLocation();
     const [searchParams, setSearchParams] = useSearchParams();
 
     return (
@@ -48,12 +47,22 @@ const AdminGoodList = ({ goods, orderBy = "_id" }) => {
                     {(goods || []).map((good) => (
                         <AdminGoodItem good={good} key={good._id} />
                     ))}
+                    {promiseStatus === "PENDING" && (
+                        <TableRow>
+                            <TableCell colSpan="7">
+                                <LinearProgress />
+                            </TableCell>
+                        </TableRow>
+                    )}
                 </TableBody>
             </Table>
         </Box>
     );
 };
 
-const CAdminGoodList = connect((state) => ({ goods: state.feed?.payload || [] }))(AdminGoodList);
+const CAdminGoodList = connect((state) => ({
+    goods: state.feed?.payload || [],
+    promiseStatus: state?.promise?.feedGoodsAll?.status || state?.promise?.feedGoodsFind?.status || null,
+}))(AdminGoodList);
 
 export { AdminGoodList, CAdminGoodList };

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

@@ -13,7 +13,7 @@ const AdminOrdersSearchPageContainer = ({ feed, orders, promiseStatus, onLoad, o
     const status = searchParams.get("status") || 0;
 
     useEffect(() => {
-        onLoad({ orderBy, text });
+        onLoad({ orderBy, text, status });
         return () => {
             onUnmount();
         };

+ 13 - 3
src/components/admin/AdminOrdersPage/AdminOrderList.js

@@ -4,7 +4,7 @@ import { connect } from "react-redux";
 import { SearchBar, SearchResults } from "../../common/SearchBar";
 import { actionOrdersFind } from "../../../actions/actionOrdersFind";
 import { actionPromiseClear } from "../../../reducers";
-import { Box, Table, TableBody, TableHead } from "@mui/material";
+import { Box, LinearProgress, Table, TableBody, TableCell, TableHead, TableRow } from "@mui/material";
 import { AdminOrderItem } from "./AdminOrderItem";
 import { createSearchParams, useNavigate, useSearchParams } from "react-router-dom";
 
@@ -15,7 +15,7 @@ const CSearchBar = connect(null, {
 
 const CSearchResults = connect((state) => ({ items: state.promise.adminOrdersFind?.payload || [] }))(SearchResults);
 
-const AdminOrderList = ({ orders, orderBy = "_id" }) => {
+const AdminOrderList = ({ orders, orderBy = "_id", promiseStatus = null }) => {
     const [searchParams, setSearchParams] = useSearchParams();
     const navigate = useNavigate();
 
@@ -47,12 +47,22 @@ const AdminOrderList = ({ orders, orderBy = "_id" }) => {
                     {(orders || []).map((order) => (
                         <AdminOrderItem order={order} key={order._id} />
                     ))}
+                    {promiseStatus === "PENDING" && (
+                        <TableRow>
+                            <TableCell colSpan="6">
+                                <LinearProgress />
+                            </TableCell>
+                        </TableRow>
+                    )}
                 </TableBody>
             </Table>
         </Box>
     );
 };
 
-const CAdminOrderList = connect((state) => ({ orders: state.feed?.payload || [] }))(AdminOrderList);
+const CAdminOrderList = connect((state) => ({
+    promiseStatus: state?.promise?.feedOrdersAll?.status || state?.promise?.feedOrdersFind?.status || null,
+    orders: state.feed?.payload || [],
+}))(AdminOrderList);
 
 export { AdminOrderList, CAdminOrderList };

+ 13 - 3
src/components/admin/AdminUsersPage/AdminUserList.js

@@ -4,7 +4,7 @@ import { connect } from "react-redux";
 import { SearchBar, SearchResults } from "../../common/SearchBar";
 import { actionUsersFind } from "../../../actions/actionUsersFind";
 import { actionPromiseClear } from "../../../reducers";
-import { Box, Table, TableBody, TableHead } from "@mui/material";
+import { Box, LinearProgress, Table, TableBody, TableCell, TableHead, TableRow } from "@mui/material";
 import { AdminUserItem } from "./AdminUserItem";
 import { createSearchParams, useNavigate, useSearchParams } from "react-router-dom";
 
@@ -15,7 +15,7 @@ const CSearchBar = connect(null, {
 
 const CSearchResults = connect((state) => ({ items: state.promise.adminUsersFind?.payload || [] }))(SearchResults);
 
-const AdminUserList = ({ users, orderBy = "_id" }) => {
+const AdminUserList = ({ users, orderBy = "_id", promiseStatus = null }) => {
     const [searchParams, setSearchParams] = useSearchParams();
     const navigate = useNavigate();
     return (
@@ -46,12 +46,22 @@ const AdminUserList = ({ users, orderBy = "_id" }) => {
                     {(users || []).map((user) => (
                         <AdminUserItem user={user} key={user._id} />
                     ))}
+                    {promiseStatus === "PENDING" && (
+                        <TableRow>
+                            <TableCell colSpan="6">
+                                <LinearProgress />
+                            </TableCell>
+                        </TableRow>
+                    )}
                 </TableBody>
             </Table>
         </Box>
     );
 };
 
-const CAdminUserList = connect((state) => ({ users: state.feed?.payload || [] }))(AdminUserList);
+const CAdminUserList = connect((state) => ({
+    promiseStatus: state?.promise?.feedUsersAll?.status || state?.promise?.feedUsersFind?.status || null,
+    users: state.feed?.payload || [],
+}))(AdminUserList);
 
 export { AdminUserList, CAdminUserList };

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

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

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

@@ -1,6 +1,5 @@
 import { useState, useEffect, useContext } from "react";
 import { connect } from "react-redux";
-// import { actionRegister } from 'actions';
 import { MdVisibility, MdVisibilityOff } from "react-icons/md";
 import { Box, Button, IconButton, TextField, Typography } from "@mui/material";
 import { useFormik } from "formik";
@@ -143,7 +142,7 @@ export const RegisterForm = ({ serverErrors, promiseStatus, onRegister, onLoginB
                 fullWidth
                 sx={{ mt: 2 }}
             >
-                Зарегистрироваться
+                Зареєструватися
             </Button>
             <Button variant="text" onClick={onLoginButtonClick}>
                 <Typography>Увійти</Typography>

+ 1 - 1
src/components/common/DropZone/index.js

@@ -12,7 +12,7 @@ export const DropZone = ({ onFileDrop, children }) => {
 
     return (
         <Box {...getRootProps({ className: "Dropzone" })}>
-            {/* <input {...getInputProps()} /> */}
+            <input {...getInputProps()} />
             {children}
         </Box>
     );

+ 0 - 1
src/components/common/GoodList/index.js

@@ -1,5 +1,4 @@
 import { Box, Grid } from "@mui/material";
-import { useEffect } from "react";
 import { GoodCard } from "../GoodCard";
 
 export const GoodList = ({ goods = [] } = {}) => {

+ 1 - 0
src/components/common/SearchBar/SearchCategoryResultItem.js

@@ -1,5 +1,6 @@
 import { Link } from "react-router-dom";
 import { Stack, Typography } from "@mui/material";
+
 export const SearchCategoryResultItem = ({ category, onClick, link = "" } = {}) => {
     const { _id = null, name = "" } = category || {};
 

+ 1 - 1
src/components/common/SearchBar/SearchUserResultItem.js

@@ -1,5 +1,5 @@
 import { Link } from "react-router-dom";
-import { Box, Grid, Stack, Typography } from "@mui/material";
+import { Box, Grid, Typography } from "@mui/material";
 import { backendURL, mediaURL } from "../../../helpers";
 import defaultAvatarImage from "../../../images/default-avatar-image.png";
 import { AiFillPlusCircle, AiOutlineMinusCircle } from "react-icons/ai";

+ 0 - 3
src/components/common/SearchBar/index.js

@@ -1,7 +1,4 @@
 import { SearchBar, CSearchBar } from "./SearchBar";
 
 import { SearchResults, CSearchResults } from "./SearchResults";
-// import SearchGoodResultItem from './SearchGoodResultItem';
-
-// export { SearchBar, SearchResults, CSearchResults, SearchGoodResultItem };
 export { SearchBar, CSearchBar, SearchResults, CSearchResults };

+ 1 - 1
src/components/layout/Aside/index.js

@@ -29,7 +29,7 @@ const Aside = ({ children }) => (
 
         <Routes>
             <Route
-                path="/admin/orders"
+                path="/admin/orders*"
                 exact
                 element={
                     <Box className="body" mt={4}>

+ 9 - 0
src/index.scss

@@ -14,7 +14,13 @@
         overflow: hidden;
         & .ProfileImage {
             max-width: 100%;
+            min-height: 400px;
+
+            & img {
+                object-fit: contain;
+            }
         }
+
         & .letter {
             z-index: 2;
             display: block;
@@ -94,6 +100,9 @@
         width: 100%;
         display: flex;
 
+        & img {
+            object-fit: contain;
+        }
         & .content {
             flex: 1;
         }

+ 74 - 15
src/reducers/feedReducer.js

@@ -7,6 +7,7 @@ import { actionOrdersFind } from "../actions/actionOrdersFind";
 import { actionCategoryGoods } from "../actions/actionCategoryGoods";
 import { actionUsersFind } from "../actions/actionUsersFind";
 import { actionUsersAll } from "../actions/actionUsersAll";
+import { delay, put, takeLeading } from "redux-saga/effects";
 
 function feedReducer(state = { payload: [] }, { type, payload = [] }) {
     if (type === "FEED_ADD") {
@@ -24,29 +25,87 @@ function feedReducer(state = { payload: [] }, { type, payload = [] }) {
 
 const actionFeedAdd = (payload) => ({ type: "FEED_ADD", payload });
 const actionFeedClear = () => ({ type: "FEED_CLEAR" });
-const actionFeedGoods = ({ skip = 0, orderBy = "_id" }) => actionGoodsAll({ skip, limit: 10, promiseName: "feedGoodsAll", orderBy });
 
-const actionFeedCategoryGoods = ({ skip = 0, orderBy = "_id", category }) =>
-    actionCategoryGoods({ skip, limit: 8, promiseName: "feedCategoryGoods", orderBy, category });
+const actionFeedGoods = ({ skip = 0, orderBy = "_id" }) => ({ type: "FEED_GOODS", payload: { skip, orderBy } });
+function* feedGoodsWorker(action) {
+    const { skip = 0, orderBy = "_id" } = action.payload || {};
+    yield put(actionGoodsAll({ skip, limit: 10, promiseName: "feedGoodsAll", orderBy }));
+    yield delay(500);
+}
+
+const actionFeedCategoryGoods = ({ skip = 0, orderBy = "_id", category }) => ({
+    type: "FEED_CATEGORY_GOODS",
+    payload: { skip, orderBy, category },
+});
+function* feedCategoryGoodsWorker(action) {
+    const { skip = 0, orderBy = "_id", category = {} } = action.payload || {};
+    yield put(actionCategoryGoods({ skip, limit: 8, promiseName: "feedCategoryGoods", orderBy, category }));
+    yield delay(500);
+}
 
-const actionFeedGoodsFind = ({ skip = 0, text = "", orderBy = "_id" }) =>
-    actionGoodsFind({ skip, limit: 10, promiseName: "feedGoodsFind", text, orderBy });
+const actionFeedGoodsFind = ({ skip = 0, text = "", orderBy = "_id" }) => ({ type: "FEED_GOODS_FIND", payload: { skip, text, orderBy } });
+function* feedGoodsFindWorker(action) {
+    const { skip = 0, text = "", orderBy = "_id" } = action.payload || {};
+    yield put(actionGoodsFind({ skip, limit: 10, promiseName: "feedGoodsFind", text, orderBy }));
+    yield delay(500);
+}
+
+const actionFeedCatsFind = ({ skip = 0, text = "", orderBy = "_id" }) => ({ type: "FEED_CATS_FIND", payload: { skip, text, orderBy } });
+function* feedCatsFindWorker(action) {
+    const { skip = 0, text = "", orderBy = "_id" } = action.payload || {};
+    yield put(actionCatsFind({ skip, promiseName: "feedCatsFind", text, limit: 10, orderBy }));
+    yield delay(500);
+}
 
-const actionFeedCatsFind = ({ skip = 0, text = "", orderBy = "_id" }) =>
-    actionCatsFind({ skip, promiseName: "feedCatsFind", text, limit: 10, orderBy });
+const actionFeedCats = ({ skip = 0, orderBy = "_id" }) => ({ type: "FEED_CATS", payload: { skip, orderBy } });
+function* feedCatsWorker(action) {
+    const { skip = 0, orderBy = "_id" } = action.payload || {};
+    yield put(actionCatAll({ promiseName: "feedCatAll", skip, limit: 10, orderBy }));
+    yield delay(500);
+}
 
-const actionFeedCats = ({ skip = 0, orderBy = "_id" }) => actionCatAll({ promiseName: "feedCatAll", skip, limit: 10, orderBy });
+const actionFeedOrders = ({ skip = 0, orderBy = "_id", status = 0 }) => ({ type: "FEED_ORDERS", payload: { skip, orderBy, status } });
+function* feedOrdersWorker(action) {
+    const { skip = 0, orderBy = "_id", status = 0 } = action.payload || {};
+    yield put(actionOrdersAll({ skip, limit: 10, promiseName: "feedOrdersAll", orderBy, status }));
+    yield delay(500);
+}
 
-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" }) => ({
+    type: "FEED_ORDERS_FIND",
+    payload: { skip, text, orderBy, status },
+});
+function* feedOrdersFindWorker(action) {
+    const { skip = 0, text = "", orderBy = "_id", status = "0" } = action.payload || {};
+    yield put(actionOrdersFind({ skip, limit: 10, promiseName: "feedOrdersFind", text, orderBy, status }));
+    yield delay(500);
+}
 
-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" }) => ({ type: "FEED_USERS_FIND", payload: { skip, text, orderBy } });
+function* feedUsersFindWorker(action) {
+    const { skip = 0, text = "", orderBy = "_id" } = action.payload || {};
+    yield put(actionUsersFind({ skip, promiseName: "feedUsersFind", text, limit: 10, orderBy }));
+    yield delay(500);
+}
 
-const actionFeedUsersFind = ({ skip = 0, text = "", orderBy = "_id" }) =>
-    actionUsersFind({ skip, promiseName: "feedUsersFind", text, limit: 10, orderBy });
+const actionFeedUsers = ({ skip = 0, orderBy = "_id" }) => ({ type: "FEED_USERS", payload: { skip, orderBy } });
+function* feedUsersWorker(action) {
+    const { skip = 0, orderBy = "_id" } = action.payload || {};
+    yield put(actionUsersAll({ promiseName: "feedUsersAll", skip, limit: 10, orderBy }));
+    yield delay(500);
+}
 
-const actionFeedUsers = ({ skip = 0, orderBy = "_id" }) => actionUsersAll({ promiseName: "feedUsersAll", skip, limit: 10, orderBy });
+export function* feedWatcher() {
+    yield takeLeading("FEED_GOODS", feedGoodsWorker);
+    yield takeLeading("FEED_CATEGORY_GOODS", feedCategoryGoodsWorker);
+    yield takeLeading("FEED_GOODS_FIND", feedGoodsFindWorker);
+    yield takeLeading("FEED_CATS_FIND", feedCatsFindWorker);
+    yield takeLeading("FEED_CATS", feedCatsWorker);
+    yield takeLeading("FEED_ORDERS", feedOrdersWorker);
+    yield takeLeading("FEED_ORDERS_FIND", feedOrdersFindWorker);
+    yield takeLeading("FEED_USERS_FIND", feedUsersFindWorker);
+    yield takeLeading("FEED_USERS", feedUsersWorker);
+}
 
 export {
     actionFeedCats,

+ 3 - 3
src/reducers/index.js

@@ -1,6 +1,5 @@
 import { createStore, combineReducers, applyMiddleware } from "redux";
-import { all, put, takeEvery, takeLatest, takeLeading, select } from "redux-saga/effects";
-import thunk from "redux-thunk";
+import { all } from "redux-saga/effects";
 import createSagaMiddleware from "redux-saga";
 import { authReducer, actionAuthLogin, actionAuthLogout, authWatcher } from "./authReducer";
 import {
@@ -25,6 +24,7 @@ import {
     feedReducer,
     actionFeedUsers,
     actionFeedUsersFind,
+    feedWatcher,
 } from "./feedReducer";
 
 export { cartReducer, actionCartAdd, actionCartChange, actionCartDelete, actionCartClear };
@@ -56,7 +56,7 @@ export const store = createStore(
 );
 
 function* rootSaga() {
-    yield all([promiseWatcher(), authWatcher()]);
+    yield all([promiseWatcher(), authWatcher(), feedWatcher()]);
 }
 
 sagaMiddleware.run(rootSaga);

+ 2 - 0
src/reducers/promiseReducer.js

@@ -2,6 +2,7 @@ import { put, takeEvery, takeLatest } from "redux-saga/effects";
 import { aboutMeWorker } from "../actions/actionAboutMe";
 import { catByIdFullWorker } from "../actions/actionCatByIdFull";
 import { categoriesPageWorker } from "../actions/actionCategoriesPage";
+import { categoriesSearchPageWorker } from "../actions/actionCategoriesSearchPage";
 import { categoryGoodsWorker } from "../actions/actionCategoryGoods";
 import { categoryPageWorker } from "../actions/actionCategoryPage";
 import { categoryUpdateWorker } from "../actions/actionCategoryUpdate";
@@ -81,4 +82,5 @@ export function* promiseWatcher() {
     yield takeLatest("USERS_SEARCH_PAGE", usersSearchPageWorker);
     yield takeLatest("USER_UPDATE", userUpdateWorker);
     yield takeEvery("PROMISES_CLEAR", promisesClearWorker);
+    yield takeLatest("CATEGORIES_SEARCH_PAGE", categoriesSearchPageWorker);
 }