Browse Source

+SearchPages for admin

ilya_shyian 1 year ago
parent
commit
0d30b8238b

+ 9 - 9
src/App.js

@@ -1,12 +1,12 @@
-import logo from './logo.svg';
-import './App.css';
-import { Box } from '@mui/material';
-import { BrowserRouter } from 'react-router-dom';
-import { Router } from 'react-router-dom';
-import { Root } from './components/Root';
-import { Provider } from 'react-redux';
-import { store } from './reducers';
-import { UIContextProvider } from './components/UIContext';
+import logo from "./logo.svg";
+import "./App.css";
+import { Box } from "@mui/material";
+import { BrowserRouter } from "react-router-dom";
+import { Router } from "react-router-dom";
+import { Root } from "./components/Root";
+import { Provider } from "react-redux";
+import { store } from "./reducers";
+import { UIContextProvider } from "./components/UIContext";
 
 function App() {
     return (

+ 40 - 0
src/actions/actionCategoryGoods.js

@@ -0,0 +1,40 @@
+import { gql } from "../helpers";
+import { actionPromise } from "../reducers";
+
+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){
+                        GoodFind(query: $query){
+                            _id name price images{
+                                _id url
+                            }
+                            categories{
+                                _id name
+                            }
+                            amount
+                        }
+                    }`,
+                    {
+                        query: JSON.stringify([
+                            {
+                                categories__in: [category._id],
+                            },
+                            {
+                                limit: !!limit ? limit : 100,
+                                skip: skip,
+                                orderBy,
+                            },
+                        ]),
+                    }
+                )
+            )
+        );
+    };

+ 2 - 2
src/actions/actionCategoryUpdate.js

@@ -1,7 +1,7 @@
 import { actionCatAll } from "./actionCatAll";
 import { actionCategoryUpsert } from "./actionCategoryUpsert";
 
-export const actionCategoryUpdate = (good) => async (dispatch, getState) => {
-  await dispatch(actionCategoryUpsert(good));
+export const actionCategoryUpdate = (category) => async (dispatch, getState) => {
+  await dispatch(actionCategoryUpsert(category));
   await dispatch(actionCatAll());
 };

+ 1 - 1
src/actions/actionCatsFind.js

@@ -19,7 +19,7 @@ export const actionCatsFind =
                     }`,
                     {
                         query: JSON.stringify([
-                            { name__contains: text },
+                            { name__contains: text, _id__contains: text },
                             {
                                 limit: !!limit ? limit : 5,
                                 skip,

+ 4 - 1
src/actions/actionGoodsFind.js

@@ -21,7 +21,10 @@ export const actionGoodsFind =
                     }`,
                     {
                         query: JSON.stringify([
-                            { name__contains: text },
+                            {
+                                name__contains: text,
+                                description__contains: text,
+                            },
                             {
                                 limit: !!limit ? limit : 5,
                                 skip,

+ 5 - 5
src/actions/actionOrderUpsert.js

@@ -10,11 +10,11 @@ export const actionOrderUpsert = (order) => async (dispatch, getState) => {
             "orderUpsert",
             gql(
                 `mutation newOrder($order:OrderInput!){
-        OrderUpsert(order:$order){
-          _id price
-        }
-      }
-      `,
+                  OrderUpsert(order:$order){
+                    _id price
+                  }
+                }
+            `,
                 {
                     order,
                 }

+ 3 - 2
src/actions/actionOrdersFind.js

@@ -2,7 +2,7 @@ import { backendURL, gql } from "../helpers";
 import { actionPromise } from "../reducers";
 
 export const actionOrdersFind =
-    ({ text = "", limit = 7, skip = 0, promiseName = "ordersFind" }) =>
+    ({ text = "", limit = 7, skip = 0, promiseName = "ordersFind", orderBy = "_id" }) =>
     async (dispatch, getState) => {
         dispatch(
             actionPromise(
@@ -18,10 +18,11 @@ export const actionOrdersFind =
                     }`,
                     {
                         query: JSON.stringify([
-                            { name__contains: text },
+                            { owner__username__contains: text, _id__contains: text, status__contains: text },
                             {
                                 limit: !!limit ? limit : 5,
                                 skip,
+                                orderBy,
                             },
                         ]),
                     }

+ 21 - 20
src/components/GoodsPage/index.js

@@ -1,20 +1,19 @@
-import { Grid, Stack, Typography, Divider } from '@mui/material';
-import { Box } from '@mui/system';
-import { connect, useDispatch } from 'react-redux';
-import { useParams } from 'react-router-dom';
-import { GoodCard } from '../common/GoodCard';
-import { GoodList } from '../common/GoodList';
-import { SubCategories } from './SubCategories';
-import { SortOptions } from '../common/SortOptions';
-import { actionCatById } from '../../actions/actionCatById';
-import { useEffect } from 'react';
+import { Grid, Stack, Typography, Divider } from "@mui/material";
+import { Box } from "@mui/system";
+import { connect, useDispatch } from "react-redux";
+import { useParams, useSearchParams } from "react-router-dom";
+import { GoodCard } from "../common/GoodCard";
+import { GoodList } from "../common/GoodList";
+import { SubCategories } from "./SubCategories";
+import { SortOptions } from "../common/SortOptions";
+import { actionCatById } from "../../actions/actionCatById";
+import { useEffect } from "react";
+import { actionCategoryGoods } from "../../actions/actionCategoryGoods";
+
+const GoodsPage = ({ category = {}, goods = [] }) => {
+    const { name = "", subcategories = [] } = category || {};
+    const [searchParams, setSearchParams] = useSearchParams();
 
-const GoodsPage = ({ category = {} }) => {
-    const { goods = [], name = '', subcategories = [] } = category || {};
-    useEffect(() => {
-        console.log(category);
-    }, [category]);
-    const dispatch = useDispatch();
     return (
         <Box className="GoodsPage">
             <Box>
@@ -23,13 +22,15 @@ const GoodsPage = ({ category = {} }) => {
                 </Typography>
             </Box>
 
-            <Divider className="Divider" />
+            {name && <Divider className="Divider" />}
             <Stack>
                 <Box className="sortOptionsWrapper">
                     <SortOptions
-                        onClick={(option) =>
-                            category._id && dispatch(actionCatById({ _id: category._id, orderBy: option.value }))
-                        }
+                        defaultOption={searchParams.get("orderBy", null)}
+                        onClick={(option) => {
+                            searchParams.set("orderBy", option.value);
+                            setSearchParams(searchParams);
+                        }}
                     />
                 </Box>
                 {!!subcategories.length ? (

+ 97 - 11
src/components/LayoutPage/index.js

@@ -1,19 +1,20 @@
 import { Box, Grid } from "@mui/material";
 import { useEffect } from "react";
-import { connect, useDispatch } from "react-redux";
-import { Navigate, Route, Routes, useLocation, useParams } from "react-router-dom";
+import { connect, useDispatch, useSelector } from "react-redux";
+import { Navigate, Route, Routes, useLocation, useParams, useSearchParams } from "react-router-dom";
 import { actionCatById } from "../../actions/actionCatById";
 import { actionGoodById } from "../../actions/actionGoodById";
 import { actionGoodsFind } from "../../actions/actionGoodsFind";
 import { actionOrders } from "../../actions/actionOrders";
+import { actionPromiseClear, store } from "../../reducers";
+import { actionFeedAdd, actionFeedCategoryGoods, actionFeedClear, actionFeedGoodsFind } from "../../reducers/feedReducer";
 import { AdminLayoutPage } from "../admin/AdminLayoutPage";
 import { CCartPage } from "../CartPage";
-import { Error404 } from "../common/Error404";
 import { GoodList } from "../common/GoodList";
-import { CProtectedRoute, ProtectedRoute } from "../common/ProtectedRoute";
+import { CProtectedRoute } from "../common/ProtectedRoute";
 import { CDashboardPage } from "../DashboardPage";
 import { GoodPage } from "../GoodPage";
-import { CGoodsPage } from "../GoodsPage";
+import { CGoodsPage, GoodsPage } from "../GoodsPage";
 import { Aside } from "../layout/Aside";
 import Content from "../layout/Content";
 import { Footer } from "../layout/Footer";
@@ -22,12 +23,96 @@ import { MainPage } from "../MainPage";
 
 const GoodsPageContainer = () => {
     const params = useParams();
+    const category = useSelector((state) => state.promise?.catById?.payload || null);
+    const goods = useSelector((state) => state.promise?.feedCategoryGoods?.payload || []);
+    const feed = useSelector((state) => state.feed?.payload || []);
     const dispatch = useDispatch();
-    if (params._id) {
-        dispatch(actionCatById({ _id: params._id }));
-    }
+    const [searchParams] = useSearchParams();
+    const orderBy = searchParams.get("orderBy") || "_id";
 
-    return <CGoodsPage />;
+    useEffect(() => {
+        if (params._id) {
+            dispatch(actionCatById({ _id: params._id }));
+        }
+
+        return () => {
+            dispatch(actionPromiseClear("catById"));
+        };
+    }, [params._id]);
+
+    useEffect(() => {
+        dispatch(actionFeedClear());
+        dispatch(actionPromiseClear("feedCategoryGoods"));
+        dispatch(actionFeedCategoryGoods({ category, orderBy, skip: 0 }));
+    }, [orderBy, category]);
+
+    useEffect(() => {
+        dispatch(actionFeedCategoryGoods({ skip: goods?.length || 0, orderBy }));
+        window.onscroll = (e) => {
+            if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
+                const {
+                    feed,
+                    promise: { feedCategoryGoods },
+                } = store.getState();
+
+                if (feedCategoryGoods.status !== "PENDING") {
+                    dispatch(actionFeedCategoryGoods({ skip: feed.payload?.length || 0, orderBy }));
+                }
+            }
+        };
+        return () => {
+            dispatch(actionFeedClear());
+            dispatch(actionPromiseClear("feedCategoryGoods"));
+            window.onscroll = null;
+        };
+    }, []);
+
+    useEffect(() => {
+        if (goods?.length) store.dispatch(actionFeedAdd(goods));
+    }, [goods]);
+    return <CGoodsPage goods={feed} />;
+};
+
+const GoodsSearchPageContainer = () => {
+    const goods = useSelector((state) => state.promise?.feedGoodsFind?.payload || []);
+    const feed = useSelector((state) => state.feed?.payload || []);
+    const dispatch = useDispatch();
+    const [searchParams] = useSearchParams();
+    const orderBy = searchParams.get("orderBy") || "_id";
+    const text = searchParams.get("text") || "";
+
+    useEffect(() => {
+        dispatch(actionFeedClear());
+        dispatch(actionPromiseClear("feedGoodsFind"));
+        dispatch(actionFeedGoodsFind({ text, orderBy, skip: 0 }));
+    }, [orderBy, text]);
+
+    useEffect(() => {
+        dispatch(actionFeedGoodsFind({ text, skip: goods?.length || 0, orderBy }));
+        window.onscroll = (e) => {
+            if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
+                const {
+                    feed,
+                    promise: { feedGoodsFind },
+                } = store.getState();
+
+                if (feedGoodsFind.status !== "PENDING") {
+                    dispatch(actionFeedGoodsFind({ text, skip: feed.payload?.length || 0, orderBy }));
+                }
+            }
+        };
+        return () => {
+            dispatch(actionFeedClear());
+            dispatch(actionPromiseClear("feedGoodsFind"));
+            window.onscroll = null;
+        };
+    }, []);
+
+    useEffect(() => {
+        if (goods?.length) store.dispatch(actionFeedAdd(goods));
+    }, [goods]);
+
+    return <GoodsPage goods={feed} />;
 };
 
 const GoodPageContainer = () => {
@@ -63,12 +148,12 @@ export const LayoutPage = () => {
         <Box className="LayoutPage">
             <Header />
             <Grid container columns={14} rows={1}>
-                {!!location.pathname.match(/(\/categor)|(\/good)|(\/order)|(\/admin)|(\/dashboard)+/) && (
+                {!!location.pathname.match(/(\/categor)|(\/good)|(\/order)|(\/admin)+/) && (
                     <Grid xs={3} item>
                         <Aside />
                     </Grid>
                 )}
-                <Grid xs={location.pathname.match(/(\/categor)|(\/good)|(\/order)|(\/admin)|(\/dashboard)+/) ? 11 : 14} item>
+                <Grid xs={location.pathname.match(/(\/categor)|(\/good)|(\/order)|(\/admin)+/) ? 11 : 14} item>
                     <Content>
                         <Routes>
                             <Route path="/" exact element={<MainPage />} />
@@ -85,6 +170,7 @@ export const LayoutPage = () => {
                             <Route path="/category/:_id" element={<GoodsPageContainer />} />
                             <Route path="/category/" element={<GoodsPageContainer />} />
                             <Route path="/good/:_id" element={<GoodPageContainer />} />
+                            <Route path="/goods/search" element={<GoodsSearchPageContainer />} />
                             <Route
                                 path="/admin/*"
                                 exact

+ 5 - 5
src/components/MainPage/index.js

@@ -1,8 +1,8 @@
-import { Box, Stack, Typography } from '@mui/material';
-import { useEffect } from 'react';
-import { useSelector } from 'react-redux';
-import MainPageImage from '../../images/main-page-image.png';
-import { GoodCardSlider } from '../common/GoodCardSlider';
+import { Box, Stack, Typography } from "@mui/material";
+import { useEffect } from "react";
+import { useSelector } from "react-redux";
+import MainPageImage from "../../images/main-page-image.png";
+import { GoodCardSlider } from "../common/GoodCardSlider";
 
 const MainPage = () => {
     const popularGoods = useSelector((state) => state.promise?.goodsPopular?.payload || []);

+ 10 - 10
src/components/Root/index.js

@@ -1,14 +1,14 @@
-import { Navigate, Route, Routes } from 'react-router-dom';
+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, useSelector } from 'react-redux';
+import { Box } from "@mui/material";
+import styles from "react-responsive-carousel/lib/styles/carousel.min.css";
+import { actionPageStart } from "../../actions/actionPageStart";
+import { useDispatch, useSelector } from "react-redux";
 
-import { AuthPage } from '../AuthPage';
-import { LayoutPage } from '../LayoutPage';
-import { CProtectedRoute } from '../common/ProtectedRoute';
-import { Error404 } from '../common/Error404';
+import { AuthPage } from "../AuthPage";
+import { LayoutPage } from "../LayoutPage";
+import { CProtectedRoute } from "../common/ProtectedRoute";
+import { Error404 } from "../common/Error404";
 
 const Root = () => {
     const dispatch = useDispatch();
@@ -20,7 +20,7 @@ const Root = () => {
                 <Route
                     path="/auth"
                     element={
-                        <CProtectedRoute roles={['anon']} fallback="/admin">
+                        <CProtectedRoute roles={["anon"]} fallback="/admin">
                             <AuthPage />
                         </CProtectedRoute>
                     }

+ 20 - 14
src/components/admin/AdminCategoriesPage/AdminCategoryList.js

@@ -1,31 +1,37 @@
-import { AdminCategoryItem } from './AdminCategoryItem';
-import { AdminCategoryListHeader } from './AdminCategoryListHeader';
-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 { useEffect, useState } from 'react';
-import { createSearchParams, useLocation, useNavigate } from 'react-router-dom';
+import { AdminCategoryItem } from "./AdminCategoryItem";
+import { AdminCategoryListHeader } from "./AdminCategoryListHeader";
+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 { useEffect, useState } from "react";
+import { createSearchParams, useLocation, useNavigate, useSearchParams } from "react-router-dom";
 
 const CSearchBar = connect(null, {
-    onSearch: (text) => actionCatsFind({ promiseName: 'adminCatsFind', text, limit: 5 }),
-    onSearchButtonClick: () => actionPromiseClear('adminCatsFind'),
+    onSearch: (text) => actionCatsFind({ promiseName: "adminCatsFind", text, limit: 5 }),
+    onSearchEnd: () => actionPromiseClear("adminCatsFind"),
 })(SearchBar);
 
 const CSearchResults = connect((state) => ({ items: state.promise.adminCatsFind?.payload || [] }))(SearchResults);
 
-const AdminCategoryList = ({ categories, orderBy = '_id' } = {}) => {
+const AdminCategoryList = ({ categories, orderBy = "_id" } = {}) => {
     const navigate = useNavigate();
     const location = useLocation();
+    const [searchParams, setSearchParams] = useSearchParams();
 
     return (
         <Box className="AdminCategoryList">
             <Box className="searchBarWrapper">
                 <CSearchBar
                     render={CSearchResults}
-                    searchLink="/admin/categories/search/"
-                    renderParams={{ itemLink: '/admin/category/' }}
+                    searchLink="/admin/categories/search"
+                    renderParams={{ itemLink: "/admin/category/" }}
+                    onSearchButtonClick={(text) => {
+                        searchParams.set("text", text);
+                        setSearchParams(searchParams);
+                        navigate({ pathname: "/admin/categories/search", search: createSearchParams(searchParams).toString() });
+                    }}
                 />
             </Box>
             <Table>

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

@@ -1,32 +1,38 @@
-import { AdminGoodListHeader } from './AdminGoodListHeader';
-import { AdminGoodItem } from './AdminGoodItem';
-import { connect } from 'react-redux';
-import { useEffect, useState } from 'react';
+import { AdminGoodListHeader } from "./AdminGoodListHeader";
+import { AdminGoodItem } from "./AdminGoodItem";
+import { connect } from "react-redux";
+import { useEffect, useState } from "react";
 
-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 } from 'react-router-dom';
+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";
 
 const CSearchBar = connect(null, {
-    onSearch: (text) => actionGoodsFind({ promiseName: 'adminGoodsFind', text, limit: 5 }),
-    onSearchButtonClick: () => actionPromiseClear('adminGoodsFind'),
+    onSearch: (text) => actionGoodsFind({ promiseName: "adminGoodsFind", text, limit: 5 }),
+    onSearchEnd: () => actionPromiseClear("adminGoodsFind"),
 })(SearchBar);
 
 const CSearchResults = connect((state) => ({ items: state.promise.adminGoodsFind?.payload || [] }))(SearchResults);
 
-const AdminGoodList = ({ goods, orderBy = '_id' }) => {
+const AdminGoodList = ({ goods, orderBy = "_id" }) => {
     const navigate = useNavigate();
     const location = useLocation();
+    const [searchParams, setSearchParams] = useSearchParams();
 
     return (
         <Box className="AdminGoodList">
             <Box className="searchBarWrapper">
                 <CSearchBar
                     render={CSearchResults}
-                    searchLink="/admin/goods/search/"
-                    renderParams={{ itemLink: '/admin/good/' }}
+                    searchLink="/admin/goods/search"
+                    renderParams={{ itemLink: "/admin/good/" }}
+                    onSearchButtonClick={(text) => {
+                        searchParams.set("text", text);
+                        setSearchParams(searchParams);
+                        navigate({ pathname: "/admin/goods/search", search: createSearchParams(searchParams).toString() });
+                    }}
                 />
             </Box>
             <Table>

+ 135 - 3
src/components/admin/AdminLayoutPage/index.js

@@ -1,12 +1,18 @@
-import { Box, Container } from "@mui/material";
+import { Box } from "@mui/material";
 import { useEffect } from "react";
 import { connect, useDispatch, useSelector } from "react-redux";
 import { Navigate, Route, Routes, useParams, useSearchParams } from "react-router-dom";
 import { actionGoodById } from "../../../actions/actionGoodById";
 import { actionCatById } from "../../../actions/actionCatById";
-import { actionPromiseClear, store, actionFeedCats } from "../../../reducers";
+import {
+    actionPromiseClear,
+    store,
+    actionFeedCats,
+    actionFeedGoodsFind,
+    actionFeedOrdersFind,
+    actionFeedCatsFind,
+} from "../../../reducers";
 import { actionFeedAdd, actionFeedClear, actionFeedGoods, actionFeedOrders } from "../../../reducers/feedReducer";
-import { CProtectedRoute } from "../../common/ProtectedRoute";
 import { CAdminGoodPage } from "../AdminGoodPage";
 import { AdminGoodsPage } from "../AdminGoodsPage";
 import { AdminCategoriesPage } from "../AdminCategoriesPage";
@@ -73,6 +79,47 @@ const AdminCategoriesPageContainer = ({ cats }) => {
     return <AdminCategoriesPage orderBy={orderBy} />;
 };
 
+const AdminCategoriesSearchPageContainer = () => {
+    const categories = useSelector((state) => state.promise?.feedCatsFind?.payload || []);
+    const dispatch = useDispatch();
+    const [searchParams] = useSearchParams();
+    const orderBy = searchParams.get("orderBy") || "_id";
+    const text = searchParams.get("text") || "";
+
+    useEffect(() => {
+        dispatch(actionFeedClear());
+        dispatch(actionPromiseClear("feedCatsFind"));
+        dispatch(actionFeedCatsFind({ text, orderBy, skip: 0 }));
+    }, [orderBy, text]);
+
+    useEffect(() => {
+        dispatch(actionFeedCatsFind({ text, skip: categories?.length || 0, orderBy }));
+        window.onscroll = (e) => {
+            if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
+                const {
+                    feed,
+                    promise: { feedCatsFind },
+                } = store.getState();
+
+                if (feedCatsFind.status !== "PENDING") {
+                    dispatch(actionFeedCatsFind({ text, skip: feed.payload?.length || 0, orderBy }));
+                }
+            }
+        };
+        return () => {
+            dispatch(actionFeedClear());
+            dispatch(actionPromiseClear("feedCatsFind"));
+            window.onscroll = null;
+        };
+    }, []);
+
+    useEffect(() => {
+        if (categories?.length) store.dispatch(actionFeedAdd(categories));
+    }, [categories]);
+
+    return <AdminCategoriesPage orderBy={orderBy} />;
+};
+
 const AdminGoodPageContainer = () => {
     const params = useParams();
     const dispatch = useDispatch();
@@ -127,6 +174,47 @@ const AdminGoodsPageContainer = ({ goods }) => {
     return <AdminGoodsPage orderBy={orderBy} />;
 };
 
+const AdminGoodsSearchPageContainer = () => {
+    const goods = useSelector((state) => state.promise?.feedGoodsFind?.payload || []);
+    const dispatch = useDispatch();
+    const [searchParams] = useSearchParams();
+    const orderBy = searchParams.get("orderBy") || "_id";
+    const text = searchParams.get("text") || "";
+
+    useEffect(() => {
+        dispatch(actionFeedClear());
+        dispatch(actionPromiseClear("feedGoodsFind"));
+        dispatch(actionFeedGoodsFind({ text, orderBy, skip: 0 }));
+    }, [orderBy, text]);
+
+    useEffect(() => {
+        dispatch(actionFeedGoodsFind({ text, skip: goods?.length || 0, orderBy }));
+        window.onscroll = (e) => {
+            if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
+                const {
+                    feed,
+                    promise: { feedGoodsFind },
+                } = store.getState();
+
+                if (feedGoodsFind.status !== "PENDING") {
+                    dispatch(actionFeedGoodsFind({ text, skip: feed.payload?.length || 0, orderBy }));
+                }
+            }
+        };
+        return () => {
+            dispatch(actionFeedClear());
+            dispatch(actionPromiseClear("feedGoodsFind"));
+            window.onscroll = null;
+        };
+    }, []);
+
+    useEffect(() => {
+        if (goods?.length) store.dispatch(actionFeedAdd(goods));
+    }, [goods]);
+
+    return <AdminGoodsPage orderBy={orderBy} />;
+};
+
 const AdminOrdersPageContainer = ({ orders }) => {
     const dispatch = useDispatch();
     const [searchParams] = useSearchParams();
@@ -168,6 +256,47 @@ const AdminOrdersPageContainer = ({ orders }) => {
     return <AdminOrdersPage orderBy={orderBy} />;
 };
 
+const AdminOrdersSearchPageContainer = () => {
+    const orders = useSelector((state) => state.promise?.feedOrdersFind?.payload || []);
+    const dispatch = useDispatch();
+    const [searchParams] = useSearchParams();
+    const orderBy = searchParams.get("orderBy") || "_id";
+    const text = searchParams.get("text") || "";
+
+    useEffect(() => {
+        dispatch(actionFeedClear());
+        dispatch(actionPromiseClear("feedOrdersFind"));
+        dispatch(actionFeedOrdersFind({ text, orderBy, skip: 0 }));
+    }, [orderBy, text]);
+
+    useEffect(() => {
+        dispatch(actionFeedOrdersFind({ text, skip: orders?.length || 0, orderBy }));
+        window.onscroll = (e) => {
+            if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 200) {
+                const {
+                    feed,
+                    promise: { feedOrdersFind },
+                } = store.getState();
+
+                if (feedOrdersFind.status !== "PENDING") {
+                    dispatch(actionFeedOrdersFind({ text, skip: feed.payload?.length || 0, orderBy }));
+                }
+            }
+        };
+        return () => {
+            dispatch(actionFeedClear());
+            dispatch(actionPromiseClear("feedOrdersFind"));
+            window.onscroll = null;
+        };
+    }, []);
+
+    useEffect(() => {
+        if (orders?.length) store.dispatch(actionFeedAdd(orders));
+    }, [orders]);
+
+    return <AdminOrdersPage orderBy={orderBy} />;
+};
+
 const AdminOrderPageContainer = () => {
     const params = useParams();
     const dispatch = useDispatch();
@@ -199,12 +328,15 @@ const AdminLayoutPage = () => {
                 <Route path="/" element={<Navigate to={"/admin/goods/"} />} />
                 <Route path="/tree/" element={<CAdminCategoryTree />} />
                 <Route path="/goods/" element={<CAdminGoodsPageContainer />} />
+                <Route path="/goods/search" element={<AdminGoodsSearchPageContainer />} />
                 <Route path="/good/" element={<AdminGoodPageContainer />} />
                 <Route path="/good/:_id" element={<AdminGoodPageContainer />} />
                 <Route path="/categories/" element={<CAdminCategoriesPageContainer />} />
+                <Route path="/categories/search" element={<AdminCategoriesSearchPageContainer />} />
                 <Route path="/category/" element={<AdminCategoryPageContainer />} />
                 <Route path="/category/:_id" element={<AdminCategoryPageContainer />} />
                 <Route path="/orders/" element={<CAdminOrdersPageContainer />} />
+                <Route path="/orders/search" element={<AdminOrdersSearchPageContainer />} />
                 <Route path="/order/" element={<AdminOrderPageContainer />} />
                 <Route path="/order/:_id" element={<AdminOrderPageContainer />} />
                 <Route path="*" element={<Navigate to="/404" />} />

+ 20 - 14
src/components/admin/AdminOrdersPage/AdminOrderList.js

@@ -1,30 +1,36 @@
-import { AdminOrderListHeader } from './AdminOrderListHeader';
-import { connect } from 'react-redux';
+import { AdminOrderListHeader } from "./AdminOrderListHeader";
+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 { AdminOrderItem } from './AdminOrderItem';
-import { createSearchParams, useLocation, useNavigate, useSearchParams } from 'react-router-dom';
+import { SearchBar, SearchResults } from "../../common/SearchBar";
+import { actionOrdersFind } from "../../../actions/actionOrdersFind";
+import { actionPromiseClear } from "../../../reducers";
+import { Box, Table, TableBody, TableHead } from "@mui/material";
+import { AdminOrderItem } from "./AdminOrderItem";
+import { createSearchParams, useNavigate, useSearchParams } from "react-router-dom";
 
 const CSearchBar = connect(null, {
-    onSearch: (text) => actionOrdersFind({ promiseName: 'adminOrdersFind', text, limit: 5 }),
-    onSearchButtonClick: () => actionPromiseClear('adminOrdersFind'),
+    onSearch: (text) => actionOrdersFind({ promiseName: "adminOrdersFind", text, limit: 5 }),
+    onSearchEnd: () => actionPromiseClear("adminOrdersFind"),
 })(SearchBar);
 
 const CSearchResults = connect((state) => ({ items: state.promise.adminOrdersFind?.payload || [] }))(SearchResults);
 
-const AdminOrderList = ({ orders, orderBy = '_id' }) => {
+const AdminOrderList = ({ orders, orderBy = "_id" }) => {
     const [searchParams, setSearchParams] = useSearchParams();
+    const navigate = useNavigate();
 
     return (
         <Box className="AdminOrderList">
             <Box className="searchBarWrapper">
                 <CSearchBar
                     render={CSearchResults}
-                    searchLink="/admin/orders/search/"
-                    renderParams={{ itemLink: '/admin/order/' }}
+                    searchLink="/admin/orders/search"
+                    renderParams={{ itemLink: "/admin/order/" }}
+                    onSearchButtonClick={(text) => {
+                        searchParams.set("text", text);
+                        setSearchParams(searchParams);
+                        navigate({ pathname: "/admin/orders/search", search: createSearchParams(searchParams).toString() });
+                    }}
                 />
             </Box>
             <Table>
@@ -32,7 +38,7 @@ const AdminOrderList = ({ orders, orderBy = '_id' }) => {
                     <AdminOrderListHeader
                         sort={orderBy}
                         onSortChange={(orderBy) => {
-                            searchParams.set('orderBy', orderBy);
+                            searchParams.set("orderBy", orderBy);
                             setSearchParams(searchParams);
                         }}
                     />

+ 3 - 6
src/components/common/GoodList/index.js

@@ -1,11 +1,8 @@
-import { Box, Grid } from '@mui/material';
-import { useEffect } from 'react';
-import { GoodCard } from '../GoodCard';
+import { Box, Grid } from "@mui/material";
+import { useEffect } from "react";
+import { GoodCard } from "../GoodCard";
 
 export const GoodList = ({ goods = [] } = {}) => {
-    useEffect(() => {
-        console.log(goods);
-    }, [goods]);
     return (
         <Box className="GoodList">
             <Grid container spacing={2}>

+ 38 - 29
src/components/common/SearchBar/SearchBar.js

@@ -1,28 +1,42 @@
-import { useEffect, useState, useRef } from 'react';
-import { connect, useDispatch } from 'react-redux';
-import { Link } from 'react-router-dom';
-import { AiOutlineCloseCircle } from 'react-icons/ai';
+import { useEffect, useState, useRef } from "react";
+import { connect } from "react-redux";
+import { AiOutlineCloseCircle } from "react-icons/ai";
 
-import { Box, TextField, Button, Container, Stack, IconButton } from '@mui/material';
-import { actionGoodsFind } from '../../../actions/actionGoodsFind';
-import { actionPromiseClear } from '../../../reducers';
-
-// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-// import { faSearch } from "@fortawesome/free-solid-svg-icons";
+import { Box, TextField, Button, Stack, IconButton } from "@mui/material";
+import { actionGoodsFind } from "../../../actions/actionGoodsFind";
+import { useLocation, useSearchParams } from "react-router-dom";
+import { actionPromiseClear } from "../../../reducers";
 
 export const SearchBar = ({
+    onSearchEnd,
     onSearch,
     onSearchButtonClick,
     render = null,
     renderParams = {},
-    searchLink = '/search/',
+    searchLink = "/search/",
 } = {}) => {
     const ref = useRef();
-    const [inputValue, setInputValue] = useState('');
+    const location = useLocation();
+    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;
 
+    const handleOnClick = () => {
+        if (!inputValue.trim().length) {
+            return;
+        }
+        onSearchButtonClick(inputValue);
+        setInputValue("");
+        onSearchEnd && onSearchEnd();
+    };
+
+    useEffect(() => {
+        setInputValue((searchLink === location.pathname && searchParams.get("text")) || "");
+    }, [searchParams]);
+
     useEffect(() => {
         if (inputTimeout) {
             clearTimeout(inputTimeout);
@@ -45,25 +59,28 @@ export const SearchBar = ({
             }, 700)
         );
 
-        setIsChildrenOpen(!!inputValue?.length);
-        document.addEventListener('mousedown', checkClickOutsideHeaderSearchBar);
+        touched && setIsChildrenOpen(!!inputValue?.length);
+        document.addEventListener("mousedown", checkClickOutsideHeaderSearchBar);
         return () => {
-            document.removeEventListener('mousedown', checkClickOutsideHeaderSearchBar);
+            document.removeEventListener("mousedown", checkClickOutsideHeaderSearchBar);
         };
     }, [inputValue]);
 
     return (
-        <Box className={`SearchBar ${!isChildrenOpen && 'hide'}`} ref={ref}>
+        <Box className={`SearchBar ${!isChildrenOpen && "hide"}`} ref={ref}>
             <Stack direction="row" alignItems="center">
                 <TextField
                     variant="standard"
                     value={inputValue}
                     placeholder="Пошук"
                     onChange={(e) => setInputValue(e.target.value)}
+                    onKeyDown={(e) => e.key === "Enter" && handleOnClick()}
                     className="SearchBarInput"
+                    onBlur={() => setTouched(true)}
+                    onClick={() => setTouched(true)}
                     InputProps={{
                         endAdornment: (
-                            <IconButton onClick={() => setInputValue('')} edge="end">
+                            <IconButton onClick={() => setInputValue("")} edge="end">
                                 {inputValue && <AiOutlineCloseCircle />}
                             </IconButton>
                         ),
@@ -71,15 +88,7 @@ export const SearchBar = ({
                 />
 
                 {!!inputValue ? (
-                    <Button
-                        className="Link"
-                        onClick={() => {
-                            setInputValue('');
-                            onSearchButtonClick();
-                        }}
-                        variant="text"
-                        color="inherit"
-                    >
+                    <Button className="Link" onClick={handleOnClick} variant="text" color="inherit">
                         Пошук
                     </Button>
                 ) : (
@@ -92,8 +101,8 @@ export const SearchBar = ({
                 {isChildrenOpen && (
                     <R
                         onItemClick={() => {
-                            setInputValue('');
-                            onSearchButtonClick();
+                            setInputValue("");
+                            onSearchEnd && onSearchEnd();
                         }}
                         {...renderParams}
                     />
@@ -105,5 +114,5 @@ export const SearchBar = ({
 
 export const CSearchBar = connect(null, {
     onSearch: (text) => actionGoodsFind({ text, limit: 5 }),
-    onSearchButtonClick: () => actionPromiseClear('goodsFind'),
+    onSearchEnd: () => actionPromiseClear("goodsFind"),
 })(SearchBar);

+ 8 - 7
src/components/common/SearchBar/SearchOrderResultItem.js

@@ -1,7 +1,8 @@
-import { Link } from 'react-router-dom';
-import { Box, Grid, Stack, Typography } from '@mui/material';
-const SearchOrderResultItem = ({ order, onClick, link = '' } = {}) => {
-    const { _id = null, email = '', phoneNumber = '' } = order || {};
+import { Link } from "react-router-dom";
+import { Box, Grid, Stack, Typography } from "@mui/material";
+import { statusNumber } from "../../../helpers";
+const SearchOrderResultItem = ({ order, onClick, link = "" } = {}) => {
+    const { _id = null, owner = null, status = "-" } = order || {};
 
     return (
         <Stack
@@ -11,11 +12,11 @@ const SearchOrderResultItem = ({ order, onClick, link = '' } = {}) => {
             onClick={() => onClick && onClick()}
             spacing={1}
         >
-            <Typography variant="body1">ID:{_id}</Typography>
+            <Typography variant="body1">ID: {_id}</Typography>
 
-            <Typography>Email:{email.length > 30 ? `${email.substring(0, 30)}...` : email}</Typography>
+            <Typography>Статус: {"" + order?.status?.length ? statusNumber[+order.status] : "-"}</Typography>
 
-            <Typography>Номер:{phoneNumber}</Typography>
+            <Typography>Користувач: {owner?.username || "-"}</Typography>
         </Stack>
     );
 };

+ 13 - 11
src/components/common/SortOptions/index.js

@@ -1,10 +1,12 @@
-import { Box, Button, Menu, MenuItem } from '@mui/material';
-import { useEffect, useState } from 'react';
-import { sortOptions } from '../../../helpers/sortOptions';
+import { Box, Button, Menu, MenuItem } from "@mui/material";
+import { useEffect, useState } from "react";
+import { sortOptions } from "../../../helpers/sortOptions";
 
-export const SortOptions = ({ onClick, options = sortOptions || [] } = {}) => {
+export const SortOptions = ({ onClick, options = sortOptions || [], defaultOption = null } = {}) => {
     const [anchorEl, setAnchorEl] = useState(null);
-    const [selectedOption, setSelectedOption] = useState(options[0] || null);
+    const [selectedOption, setSelectedOption] = useState(
+        options.filter((option) => option.value === defaultOption || option.key === defaultOption)[0] || options[0] || null
+    );
     const open = Boolean(anchorEl);
     const handleClick = (event) => {
         setAnchorEl(event.currentTarget);
@@ -25,9 +27,9 @@ export const SortOptions = ({ onClick, options = sortOptions || [] } = {}) => {
         <Box className="SortOptions">
             <Button
                 id="demo-positioned-button"
-                aria-controls={open ? 'demo-positioned-menu' : undefined}
+                aria-controls={open ? "demo-positioned-menu" : undefined}
                 aria-haspopup="true"
-                aria-expanded={open ? 'true' : undefined}
+                aria-expanded={open ? "true" : undefined}
                 onClick={handleClick}
             >
                 {selectedOption.label}
@@ -39,12 +41,12 @@ export const SortOptions = ({ onClick, options = sortOptions || [] } = {}) => {
                 open={open}
                 onClose={() => setAnchorEl(null)}
                 anchorOrigin={{
-                    vertical: 'top',
-                    horizontal: 'left',
+                    vertical: "top",
+                    horizontal: "left",
                 }}
                 transformOrigin={{
-                    vertical: 'top',
-                    horizontal: 'left',
+                    vertical: "top",
+                    horizontal: "left",
                 }}
             >
                 {(options || []).map((option) => (

+ 68 - 58
src/components/layout/Header/index.js

@@ -1,72 +1,82 @@
-import { AppBar, Box, Button, IconButton, Stack, TextField, Toolbar, Typography } from "@mui/material";
+import { AppBar, Box, Button, IconButton, Stack, Toolbar, Typography } from "@mui/material";
 import { useState } from "react";
-
-import { useSelector } from "react-redux";
-import { Link, Navigate, useNavigate } from "react-router-dom";
+import { useDispatch, useSelector } from "react-redux";
+import { createSearchParams, Link, useNavigate, useSearchParams } from "react-router-dom";
 import { ReactComponent as ShoppingLogo } from "../../../images/shopping-logo.svg";
+import { actionPromiseClear } from "../../../reducers";
 import { AuthModal } from "../../common/AuthModal";
-import { Ava } from "../../common/Ava";
 import { DrawerCart } from "../../common/DrawerCart/DrawerCart";
-import { CSearchBar, SearchBar } from "../../common/SearchBar";
+import { CSearchBar } from "../../common/SearchBar";
 import { CSearchResults } from "../../common/SearchBar/SearchResults";
 import { AvatarButton } from "./AvatarButton";
 import { CCartIcon } from "./CartIcon";
 import { LogoutIcon } 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 token = useSelector((state) => state?.auth?.token || null);
+    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);
 
-  return (
-    <Box className="Header">
-      <AppBar position="static" className="AppBar">
-        <Toolbar variant="dense" className="ToolBar">
-          <IconButton component={Link} to="/">
-            <ShoppingLogo className="Logo" />
-          </IconButton>
-          <Stack direction="row" spacing={2}>
-            <Button variant="text" color="inherit" component={Link} to="/">
-              <Typography variant="body1" component="div">
-                Головна
-              </Typography>
-            </Button>
-            <Button variant="text" color="inherit" component={Link} to={rootCats[0] ? `/category/${rootCats[0]._id}` : "/"}>
-              <Typography variant="body1" component="div">
-                Товари
-              </Typography>
-            </Button>
-          </Stack>
-          <Box className="SearchBarWrapper">
-            <CSearchBar render={CSearchResults} renderParams={{ itemLink: "/good/" }} />
-          </Box>
-          <Stack direction="row" spacing={3}>
-            {token ? (
-              <Stack direction="row" spacing={3}>
-                <LogoutIcon />
-                <AvatarButton onClick={() => navigate("/dashboard/")} />
-              </Stack>
-            ) : (
-              <Button variant="text" color="inherit" onClick={() => setIsAuthModalOpen(true)}>
-                <Typography variant="body1" component="div">
-                  Увійти
-                </Typography>
-              </Button>
-            )}
-            <IconButton color="inherit" className="CartLogoButton" onClick={() => setIsCartDrawerOpen(true)}>
-              <Box>
-                <CCartIcon />
-              </Box>
-            </IconButton>
-          </Stack>
-        </Toolbar>
-      </AppBar>
-      <DrawerCart isOpen={isCartDrawerOpen} onClose={() => setIsCartDrawerOpen(false)} />
-      <AuthModal open={isAuthModalOpen} onClose={() => setIsAuthModalOpen(false)} />
-    </Box>
-  );
+    return (
+        <Box className="Header">
+            <AppBar position="static" className="AppBar">
+                <Toolbar variant="dense" className="ToolBar">
+                    <IconButton component={Link} to="/">
+                        <ShoppingLogo className="Logo" />
+                    </IconButton>
+                    <Stack direction="row" spacing={2}>
+                        <Button variant="text" color="inherit" component={Link} to="/">
+                            <Typography variant="body1" component="div">
+                                Головна
+                            </Typography>
+                        </Button>
+                        <Button variant="text" color="inherit" component={Link} to={rootCats[0] ? `/category/${rootCats[0]._id}` : "/"}>
+                            <Typography variant="body1" component="div">
+                                Товари
+                            </Typography>
+                        </Button>
+                    </Stack>
+                    <Box className="SearchBarWrapper">
+                        <CSearchBar
+                            render={CSearchResults}
+                            renderParams={{ itemLink: "/good/" }}
+                            searchLink="/goods/search"
+                            onSearchButtonClick={(text) => {
+                                searchParams.set("text", text);
+                                setSearchParams(searchParams);
+                                navigate({ pathname: "/goods/search", search: createSearchParams(searchParams).toString() });
+                            }}
+                        />
+                    </Box>
+                    <Stack direction="row" spacing={3}>
+                        {token ? (
+                            <Stack direction="row" spacing={3}>
+                                <LogoutIcon />
+                                <AvatarButton onClick={() => navigate("/dashboard/")} />
+                            </Stack>
+                        ) : (
+                            <Button variant="text" color="inherit" onClick={() => setIsAuthModalOpen(true)}>
+                                <Typography variant="body1" component="div">
+                                    Увійти
+                                </Typography>
+                            </Button>
+                        )}
+                        <IconButton color="inherit" className="CartLogoButton" onClick={() => setIsCartDrawerOpen(true)}>
+                            <Box>
+                                <CCartIcon />
+                            </Box>
+                        </IconButton>
+                    </Stack>
+                </Toolbar>
+            </AppBar>
+            <DrawerCart isOpen={isCartDrawerOpen} onClose={() => setIsCartDrawerOpen(false)} />
+            <AuthModal open={isAuthModalOpen} onClose={() => setIsAuthModalOpen(false)} />
+        </Box>
+    );
 };
 
 export { Header };

+ 8 - 8
src/helpers/sortOptions.js

@@ -1,18 +1,18 @@
 export const sortOptions = [
     {
-        value: 'date',
-        label: 'Спочатку нові',
+        value: "createdAt",
+        label: "Спочатку нові",
     },
     {
-        value: '-date',
-        label: 'Спочатку старі',
+        value: "-createdAt",
+        label: "Спочатку старі",
     },
     {
-        value: 'price',
-        label: 'Від дешевих до дорогих',
+        value: "price",
+        label: "Від дешевих до дорогих",
     },
     {
-        value: '-price',
-        label: 'Від дорогих до дешевих',
+        value: "-price",
+        label: "Від дорогих до дешевих",
     },
 ];

+ 14 - 6
src/reducers/feedReducer.js

@@ -6,6 +6,7 @@ import { actionGoodsAll } from "../actions/actionGoodsAll";
 import { gql } from "../helpers";
 import { actionOrdersAll } from "../actions/actionOrdersAll";
 import { actionOrdersFind } from "../actions/actionOrdersFind";
+import { actionCategoryGoods } from "../actions/actionCategoryGoods";
 
 function feedReducer(state = { payload: [] }, { type, payload = [] }) {
     if (type === "FEED_ADD") {
@@ -29,16 +30,22 @@ const actionFeedGoods =
         await dispatch(actionGoodsAll({ skip, limit: 15, promiseName: "feedGoodsAll", orderBy }));
     };
 
+const actionFeedCategoryGoods =
+    ({ skip = 0, orderBy = "_id", category }) =>
+    async (dispatch, getState) => {
+        await dispatch(actionCategoryGoods({ skip, limit: 15, promiseName: "feedCategoryGoods", orderBy, category }));
+    };
+
 const actionFeedGoodsFind =
-    ({ skip = 0, text = "" }) =>
+    ({ skip = 0, text = "", orderBy = "_id" }) =>
     async (dispatch, getState) => {
-        await dispatch(actionGoodsFind({ skip, limit: 15, promiseName: "feedGoodsFind", text }));
+        await dispatch(actionGoodsFind({ skip, limit: 15, promiseName: "feedGoodsFind", text, orderBy }));
     };
 
 const actionFeedCatsFind =
-    ({ skip = 0, text = "" }) =>
+    ({ skip = 0, text = "", orderBy = "_id" }) =>
     async (dispatch, getState) => {
-        await dispatch(actionCatsFind({ skip, promiseName: "feedCatsFind", text, limit: 7 }));
+        await dispatch(actionCatsFind({ skip, promiseName: "feedCatsFind", text, limit: 7, orderBy }));
     };
 
 const actionFeedCats =
@@ -54,9 +61,9 @@ const actionFeedOrders =
     };
 
 const actionFeedOrdersFind =
-    ({ skip = 0, text = "" }) =>
+    ({ skip = 0, text = "", orderBy = "_id" }) =>
     async (dispatch, getState) => {
-        await dispatch(actionOrdersFind({ skip, limit: 15, promiseName: "feedOrdersFind", text }));
+        await dispatch(actionOrdersFind({ skip, limit: 5, promiseName: "feedOrdersFind", text, orderBy }));
     };
 
 export {
@@ -69,4 +76,5 @@ export {
     feedReducer,
     actionFeedOrders,
     actionFeedOrdersFind,
+    actionFeedCategoryGoods,
 };