Просмотр исходного кода

Partly finished feature 645

feature/645
jovan.cirkovic 3 лет назад
Родитель
Сommit
461c8a04db
34 измененных файлов: 790 добавлений и 149 удалений
  1. 1
    2
      src/AppRoutes.js
  2. 52
    0
      src/components/Cards/CategoryCard/CategoryCard.js
  3. 39
    0
      src/components/Cards/CategoryCard/CategoryCard.styled.js
  4. 21
    0
      src/components/Cards/CategoryCard/CategoryCardName/CategoryCardName.js
  5. 21
    0
      src/components/Cards/CategoryCard/CategoryCardName/CategoryCardName.styled.js
  6. 25
    0
      src/components/Cards/CategoryCard/CategoryCheckButton/CategoryCheckButton.js
  7. 17
    0
      src/components/Cards/CategoryCard/CategoryCheckButton/CategoryCheckButton.styled.js
  8. 23
    0
      src/components/Cards/CategoryCard/CategoryDetail/CategoryDetail.js
  9. 53
    0
      src/components/Cards/CategoryCard/CategoryDetail/CategoryDetail.styled.js
  10. 20
    0
      src/components/Cards/CategoryCard/CategoryEditButton/CategoryEditButton.js
  11. 39
    0
      src/components/Cards/CategoryCard/CategoryEditButton/CategoryEditButton.styled.js
  12. 20
    0
      src/components/Cards/CategoryCard/CategoryRemoveButton/CategoryRemoveButton.js
  13. 38
    0
      src/components/Cards/CategoryCard/CategoryRemoveButton/CategoryRemoveButton.styled.js
  14. 3
    1
      src/components/Cards/ProfileCard/BigProfileCard/BigProfileCard.js
  15. 2
    2
      src/components/Cards/ProfileCard/BigProfileCard/BigProfileCard.styled.js
  16. 18
    10
      src/components/Header/Header.js
  17. 63
    34
      src/components/MarketPlace/Header/Header.js
  18. 13
    0
      src/components/MarketPlace/Header/Header.styled.js
  19. 17
    4
      src/components/MarketPlace/Offers/Offers.js
  20. 16
    14
      src/components/TextFields/SearchField/SearchField.js
  21. 5
    6
      src/components/TextFields/SearchField/SearchField.styled.js
  22. 27
    5
      src/constants/adminNavigation.js
  23. 3
    0
      src/constants/pages.js
  24. 15
    4
      src/hooks/useOffers/useSearch.js
  25. 11
    0
      src/i18n/resources/rs.js
  26. 71
    0
      src/pages/AdminCategoriesPage/AdminCategoriesPage.js
  27. 32
    0
      src/pages/AdminCategoriesPage/AdminCategoriesPage.styled.js
  28. 16
    7
      src/pages/AdminHomePage/AdminHomePage.js
  29. 5
    2
      src/store/actions/filters/filtersActionConstants.js
  30. 46
    30
      src/store/actions/filters/filtersActions.js
  31. 29
    16
      src/store/reducers/filters/filtersReducer.js
  32. 15
    11
      src/store/selectors/filtersSelectors.js
  33. 11
    0
      src/util/helpers/colorHelper.js
  34. 3
    1
      src/util/helpers/routeHelpers.js

+ 1
- 2
src/AppRoutes.js Просмотреть файл

ABOUT_PAGE, ABOUT_PAGE,
ADMIN_HOME_PAGE, ADMIN_HOME_PAGE,
ADMIN_USERS_PAGE, ADMIN_USERS_PAGE,
ADMIN_CATEGORIES_PAGE,
// POLICY_PRIVACY_PAGE, // POLICY_PRIVACY_PAGE,
} from "./constants/pages"; } from "./constants/pages";
import LoginPage from "./pages/LoginPage/LoginPage"; import LoginPage from "./pages/LoginPage/LoginPage";
import AuthRoute from "./components/Router/AuthRoute"; import AuthRoute from "./components/Router/AuthRoute";
import AdminRoute from "./components/Router/AdminRoute"; import AdminRoute from "./components/Router/AdminRoute";
import AdminHomePage from "./pages/AdminHomePage/AdminHomePage"; import AdminHomePage from "./pages/AdminHomePage/AdminHomePage";
// import AdminUsersPage from "./pages/AdminUsersPage/AdminUsersPage";
// import PrivacyPolicyPage from "./pages/PrivacyPolicy/PrivacyPolicyPage"; // import PrivacyPolicyPage from "./pages/PrivacyPolicy/PrivacyPolicyPage";


const AppRoutes = () => { const AppRoutes = () => {
<AuthRoute exact path={LOGIN_PAGE} component={LoginPage} /> <AuthRoute exact path={LOGIN_PAGE} component={LoginPage} />
<AuthRoute exact path={ADMIN_LOGIN_PAGE} component={AdminLoginPage} /> <AuthRoute exact path={ADMIN_LOGIN_PAGE} component={AdminLoginPage} />
<AdminRoute path={ADMIN_HOME_PAGE} component={AdminHomePage} /> <AdminRoute path={ADMIN_HOME_PAGE} component={AdminHomePage} />
{/* <AdminRoute path={ADMIN_USERS_PAGE} component={AdminUsersPage} /> */}
<Route path={NOT_FOUND_PAGE} component={NotFoundPage} /> <Route path={NOT_FOUND_PAGE} component={NotFoundPage} />
<Route path={ERROR_PAGE} component={ErrorPage} /> <Route path={ERROR_PAGE} component={ErrorPage} />
<AuthRoute <AuthRoute

+ 52
- 0
src/components/Cards/CategoryCard/CategoryCard.js Просмотреть файл

import React from "react";
import PropTypes from "prop-types";
import {
CategoryCardContainer,
CategoryCardDetailsContainer,
CategoryCardLeftContainer,
CategoryCardRightContainer,
} from "./CategoryCard.styled";
import CategoryCardName from "./CategoryCardName/CategoryCardName";
import CategoryDetail from "./CategoryDetail/CategoryDetail";
import CategoryCheckButton from "./CategoryCheckButton/CategoryCheckButton";
import CategoryEditButton from "./CategoryEditButton/CategoryEditButton";
import CategoryRemoveButton from "./CategoryRemoveButton/CategoryRemoveButton";
import { useTranslation } from "react-i18next";

const CategoryCard = (props) => {
const { t } = useTranslation();
return (
<CategoryCardContainer>
<CategoryCardLeftContainer>
<CategoryCardName categoryName={props?.category?.name} />
<CategoryCardDetailsContainer>
<CategoryDetail
label={t("admin.categories.noOfOffers")}
value={props?.category?.offerCount}
/>
{!props.hideSecondLabel && (
<CategoryDetail
label={props?.secondLabel}
value={props?.category?.subcategories?.length}
/>
)}
</CategoryCardDetailsContainer>
</CategoryCardLeftContainer>
<CategoryCardRightContainer>
<CategoryRemoveButton />
<CategoryEditButton />
{!props.hideCheckButton && <CategoryCheckButton />}
</CategoryCardRightContainer>
</CategoryCardContainer>
);
};

CategoryCard.propTypes = {
children: PropTypes.node,
category: PropTypes.object,
hideCheckButton: PropTypes.bool,
secondLabel: PropTypes.string,
hideSecondLabel: PropTypes.bool,
};

export default CategoryCard;

+ 39
- 0
src/components/Cards/CategoryCard/CategoryCard.styled.js Просмотреть файл

import { Box } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../themes";

export const CategoryCardContainer = styled(Box)`
background: white;
height: 84px;
width: calc(100% - 10px);
margin: 5px;
margin-top: 13px;
margin-bottom: 13px;
border: 1px solid ${selectedTheme.colors.borderNormal};
border-radius: 4px;
display: flex;
flex-direction: row;
justify-content: space-between;
position: relative;
@media (max-width: 600px) {
height: 102px;
}
`;
export const CategoryCardLeftContainer = styled(Box)`
display: flex;
flex-direction: row;
`;
export const CategoryCardRightContainer = styled(Box)`
display: flex;
flex-direction: row;
`;
export const CategoryCardDetailsContainer = styled(Box)`
display: flex;
flex-direction: row;
@media (max-width: 600px) {
position: absolute;
bottom: 18px;
left: 0;
max-height: 16px;
}
`;

+ 21
- 0
src/components/Cards/CategoryCard/CategoryCardName/CategoryCardName.js Просмотреть файл

import React from "react";
import PropTypes from "prop-types";
import {
CategoryCardNameContainer,
CategoryCardNameText,
} from "./CategoryCardName.styled";

const CategoryCardName = (props) => {
return (
<CategoryCardNameContainer>
<CategoryCardNameText>{props.categoryName}</CategoryCardNameText>
</CategoryCardNameContainer>
);
};

CategoryCardName.propTypes = {
children: PropTypes.node,
categoryName: PropTypes.string,
};

export default CategoryCardName;

+ 21
- 0
src/components/Cards/CategoryCard/CategoryCardName/CategoryCardName.styled.js Просмотреть файл

import { Box, Typography } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../../themes";

export const CategoryCardNameContainer = styled(Box)`
padding: 18px;
min-width: 234px;
`;
export const CategoryCardNameText = styled(Typography)`
font-family: ${selectedTheme.fonts.textFont};
font-weight: 700;
font-size: 16px;
line-height: 21px;
padding-top: 12px;
color: ${selectedTheme.colors.primaryPurple};
cursor: pointer;
@media (max-width: 600px) {
font-size: 18px;
padding: 0;
}
`;

+ 25
- 0
src/components/Cards/CategoryCard/CategoryCheckButton/CategoryCheckButton.js Просмотреть файл

import React from "react";
import PropTypes from "prop-types";
import selectedTheme from "../../../../themes";
import { CheckButton } from "./CategoryCheckButton.styled";
import { useTranslation } from "react-i18next";

const CategoryCheckButton = () => {
const { t } = useTranslation();
return (
<CheckButton
variant={"outlined"}
buttoncolor={selectedTheme.colors.primaryPurple}
textcolor={selectedTheme.colors.primaryPurple}
style={{ fontWeight: "600" }}
>
{t("admin.categories.checkCategory")}
</CheckButton>
);
};

CategoryCheckButton.propTypes = {
category: PropTypes.any,
};

export default CategoryCheckButton;

+ 17
- 0
src/components/Cards/CategoryCard/CategoryCheckButton/CategoryCheckButton.styled.js Просмотреть файл

import styled from "styled-components";
import selectedTheme from "../../../../themes";
import { PrimaryButton } from "../../../Buttons/PrimaryButton/PrimaryButton";

export const CheckButton = styled(PrimaryButton)`
width: 224px;
height: 48px;
margin-top: 9px;
margin-right: 9px;
& button:hover {
background-color: ${selectedTheme.colors.primaryPurple} !important;
color: white !important;
}
@media (max-width: 850px) {
display: none;
}
`;

+ 23
- 0
src/components/Cards/CategoryCard/CategoryDetail/CategoryDetail.js Просмотреть файл

import React from "react";
import PropTypes from "prop-types";
import {
CategoryDetailContainer,
CategoryDetailLabel,
CategoryDetailValue,
} from "./CategoryDetail.styled";

const CategoryDetail = (props) => {
return (
<CategoryDetailContainer>
<CategoryDetailLabel>{props.label}</CategoryDetailLabel>
<CategoryDetailValue>{props.value}</CategoryDetailValue>
</CategoryDetailContainer>
);
};

CategoryDetail.propTypes = {
label: PropTypes.string,
value: PropTypes.string,
};

export default CategoryDetail;

+ 53
- 0
src/components/Cards/CategoryCard/CategoryDetail/CategoryDetail.styled.js Просмотреть файл

import { Box, Typography } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../../themes";
import { hexToRGB } from "../../../../util/helpers/colorHelper";

export const CategoryDetailContainer = styled(Box)`
/* display: flex;
flex-direction: row; */
margin-top: 20px;
margin-bottom: 20px;
min-width: 150px;
text-align: center;
border-left: 1px solid ${hexToRGB(selectedTheme.colors.primaryText, 0.13)};
@media (max-width: 600px) {
margin: 0;
min-width: 123px;
&:nth-child(1) {
border-left: 0;
}
&:nth-child(2) {
padding-left: 18px;
}
}
`;
export const CategoryDetailLabel = styled(Typography)`
font-family: ${selectedTheme.fonts.textFont};
color: ${selectedTheme.colors.primaryText};
font-size: 12px;
letter-spacing: 0.01rem;
padding-top: 14px;
padding-right: 3px;
@media (max-width: 600px) {
position: relative;
bottom: 2.5px;
padding-top: 0;
}
`;
export const CategoryDetailValue = styled(Typography)`
font-family: ${selectedTheme.fonts.textFont};
font-weight: 700;
font-size: 16px;
line-height: 21px;
padding-top: 11px;
color: ${selectedTheme.colors.primaryPurple};
@media (max-width: 600px) {
position: relative;
bottom: 2.5px;
padding: 0;
font-size: 12px;
font-weight: 600;
}
`;

+ 20
- 0
src/components/Cards/CategoryCard/CategoryEditButton/CategoryEditButton.js Просмотреть файл

import React from "react";
import PropTypes from "prop-types";
import {
CategoryEditButtonContainer,
EditIcon,
} from "./CategoryEditButton.styled";

const CategoryEditButton = () => {
return (
<CategoryEditButtonContainer>
<EditIcon />
</CategoryEditButtonContainer>
);
};

CategoryEditButton.propTypes = {
category: PropTypes.any,
};

export default CategoryEditButton;

+ 39
- 0
src/components/Cards/CategoryCard/CategoryEditButton/CategoryEditButton.styled.js Просмотреть файл

import styled from "styled-components";
import selectedTheme from "../../../../themes";
import { IconButton } from "../../../Buttons/IconButton/IconButton";
import { ReactComponent as Edit } from "../../../../assets/images/svg/edit.svg";

export const CategoryEditButtonContainer = styled(IconButton)`
width: 40px;
height: 40px;
background-color: ${selectedTheme.colors.primaryIconBackgroundColor};
border-radius: 100%;
position: relative;
top: 22px;
margin-right: 18px;
padding-top: 2px;
text-align: center;
@media (max-width: 600px) {
width: 32px;
height: 32px;
top: 18px;
right: 18px;
margin-right: 0;
padding: 0;
${(props) =>
props.vertical &&
`
display: none;
`}
& button svg {
width: 16px;
height: 16px;
position: relative;
top: -3px;
left: -1.5px;
}
}
`;
export const EditIcon = styled(Edit)`
`

+ 20
- 0
src/components/Cards/CategoryCard/CategoryRemoveButton/CategoryRemoveButton.js Просмотреть файл

import React from "react";
import PropTypes from "prop-types";
import {
CategoryRemoveButtonContainer,
RemoveIcon,
} from "./CategoryRemoveButton.styled";

const CategoryRemoveButton = () => {
return (
<CategoryRemoveButtonContainer>
<RemoveIcon />
</CategoryRemoveButtonContainer>
);
};

CategoryRemoveButton.propTypes = {
category: PropTypes.any,
};

export default CategoryRemoveButton;

+ 38
- 0
src/components/Cards/CategoryCard/CategoryRemoveButton/CategoryRemoveButton.styled.js Просмотреть файл

import styled from "styled-components";
import selectedTheme from "../../../../themes";
import { IconButton } from "../../../Buttons/IconButton/IconButton";
import { ReactComponent as Remove } from "../../../../assets/images/svg/trash.svg";

export const CategoryRemoveButtonContainer = styled(IconButton)`
width: 40px;
height: 40px;
background-color: ${selectedTheme.colors.primaryIconBackgroundColor};
border-radius: 100%;
position: relative;
top: 22px;
margin-right: 18px;
padding-top: 2px;
text-align: center;
@media (max-width: 600px) {
width: 32px;
height: 32px;
top: 18px;
right: 12px;
padding: 0;
${(props) =>
props.vertical &&
`
display: none;
`}
& button svg {
width: 16px;
height: 16px;
position: relative;
top: -3px;
left: -1.5px;
}
}
`;
export const RemoveIcon = styled(Remove)`
`

+ 3
- 1
src/components/Cards/ProfileCard/BigProfileCard/BigProfileCard.js Просмотреть файл

const blockUser = () => {}; const blockUser = () => {};
return ( return (
<> <>
<ProfileCardContainer>
<ProfileCardContainer halfwidth={props.halfwidth}>
<ProfileCardWrapper variant="outlined"> <ProfileCardWrapper variant="outlined">
<EditButton onClick={() => setEditProfileModal(true)}> <EditButton onClick={() => setEditProfileModal(true)}>
<EditIcon /> <EditIcon />
<ProfileContact profile={props.profile} isAdmin /> <ProfileContact profile={props.profile} isAdmin />
</ProfileInfoContainer> </ProfileInfoContainer>
<CheckButton <CheckButton
halfwidth={props.halfwidth}
variant={"outlined"} variant={"outlined"}
buttoncolor={selectedTheme.colors.primaryPurple} buttoncolor={selectedTheme.colors.primaryPurple}
textcolor={selectedTheme.colors.primaryPurple} textcolor={selectedTheme.colors.primaryPurple}


BigProfileCard.propTypes = { BigProfileCard.propTypes = {
profile: PropTypes.any, profile: PropTypes.any,
halfwidth: PropTypes.bool,
}; };


export default BigProfileCard; export default BigProfileCard;

+ 2
- 2
src/components/Cards/ProfileCard/BigProfileCard/BigProfileCard.styled.js Просмотреть файл

// import { ReactComponent as Location } from "../../../assets/images/svg/location.svg"; // import { ReactComponent as Location } from "../../../assets/images/svg/location.svg";


export const ProfileCardContainer = styled(Box)` export const ProfileCardContainer = styled(Box)`
width: 100%;
width: ${(props) => (props.halfwidth ? `49%` : `100%`)};
box-sizing: border-box; box-sizing: border-box;
max-height: 184px; max-height: 184px;
margin-top: 34px; margin-top: 34px;
} }
`; `;



export const MessageButton = styled(IconButton)` export const MessageButton = styled(IconButton)`
width: 40px; width: 40px;
height: 40px; height: 40px;
position: absolute; position: absolute;
bottom: 25px; bottom: 25px;
right: 9px; right: 9px;
display: ${(props) => (props.halfwidth ? `none` : `block`)};
& button:hover { & button:hover {
background-color: ${selectedTheme.colors.primaryPurple} !important; background-color: ${selectedTheme.colors.primaryPurple} !important;
color: white !important; color: white !important;

+ 18
- 10
src/components/Header/Header.js Просмотреть файл

import UserButton from "./UserButton/UserButton"; import UserButton from "./UserButton/UserButton";
import LoginButton from "./LoginButton/LoginButton"; import LoginButton from "./LoginButton/LoginButton";
import RegisterButton from "./RegisterButton/RegisterButton"; import RegisterButton from "./RegisterButton/RegisterButton";
import useIsMobile from "../../hooks/useIsMobile";


const Header = () => { const Header = () => {
const [showCreateOfferModal, setShowCreateOfferModal] = useState(false); const [showCreateOfferModal, setShowCreateOfferModal] = useState(false);
const drawerRef = useRef(null); const drawerRef = useRef(null);
const [shouldShow, setShouldShow] = useState(true); const [shouldShow, setShouldShow] = useState(true);
const routeMatch = useRouteMatch(); const routeMatch = useRouteMatch();
const { isMobile } = useIsMobile();


// Dont show header on auth routes(login, register, etc.) and admin routes // Dont show header on auth routes(login, register, etc.) and admin routes
useEffect(() => { useEffect(() => {
if (isAuthRoute() || isAdminRoute()) setShouldShow(false);
if (isAuthRoute() || (isAdminRoute() && !isMobile)) setShouldShow(false);
else setShouldShow(true); else setShouldShow(true);
}, [routeMatch]);
}, [routeMatch, isMobile]);


// Fetch mine profile on loading home page // Fetch mine profile on loading home page
useEffect(() => { useEffect(() => {
} }
// Handling search when user is on marketplace and when he is not // Handling search when user is on marketplace and when he is not
const handleSearch = (value) => { const handleSearch = (value) => {
if (!routeMatches(HOME_PAGE) && !routeMatches(BASE_PAGE)) {
const newQueryString = new URLSearchParams({ search: value });
history.push({
pathname: HOME_PAGE,
search: newQueryString.toString(),
});
if (isAdminRoute()) {
console.log("admin");
search.setSearchStringManually(value);
} else { } else {
search.searchOffersImmediately(value);
if (!routeMatches(HOME_PAGE) && !routeMatches(BASE_PAGE)) {
const newQueryString = new URLSearchParams({ search: value });
history.push({
pathname: HOME_PAGE,
search: newQueryString.toString(),
});
} else {
search.searchOffersImmediately(value);
}
} }
}; };


}; };


if (!shouldShow) { if (!shouldShow) {
return (<></>)
return <></>;
} }


return ( return (
Header.propTypes = { Header.propTypes = {
isGrid: PropTypes.bool, isGrid: PropTypes.bool,
showModalHandler: PropTypes.func, showModalHandler: PropTypes.func,
manualSearch: PropTypes.func,
}; };


export default Header; export default Header;

+ 63
- 34
src/components/MarketPlace/Header/Header.js Просмотреть файл

ButtonContainer, ButtonContainer,
// ButtonContainer, // ButtonContainer,
CategoryHeaderIcon, CategoryHeaderIcon,
CategoryIcon,
HeaderAltLocation, HeaderAltLocation,
HeaderButton, HeaderButton,
HeaderButtons, HeaderButtons,
<> <>
<SkeletonHeader skeleton={props?.skeleton} myOffers={props?.myOffers} /> <SkeletonHeader skeleton={props?.skeleton} myOffers={props?.myOffers} />
<HeaderWrapperContainer <HeaderWrapperContainer
className={props.className}
skeleton={props?.skeleton} skeleton={props?.skeleton}
isAdmin={props?.isAdmin} isAdmin={props?.isAdmin}
> >
<> <>
{!isMobile ? ( {!isMobile ? (
<HeaderTitleContainer> <HeaderTitleContainer>
{props.users ? <UserIcon /> : <SwapsHeaderIcon />}
{props.users ? (
<UserIcon />
) : props.categories ? (
<CategoryIcon />
) : (
<SwapsHeaderIcon />
)}
<HeaderTitleText> <HeaderTitleText>
{props.users {props.users
? t("admin.users.headerTitle") ? t("admin.users.headerTitle")
: props.categories
? t("admin.categories.headerTitle")
: t("header.myOffers")} : t("header.myOffers")}
</HeaderTitleText> </HeaderTitleText>
</HeaderTitleContainer> </HeaderTitleContainer>
) : ( ) : (
<ButtonContainer onClick={goBack}>
<ArrowButton side={"left"}></ArrowButton>
<HeaderText>{t("messages.goBack")}</HeaderText>
</ButtonContainer>
!props.hideBackButton && (
<ButtonContainer onClick={goBack}>
<ArrowButton side={"left"}></ArrowButton>
<HeaderText>{t("messages.goBack")}</HeaderText>
</ButtonContainer>
)
)} )}
</> </>
)} )}
{/* ^^^^^^ */} {/* ^^^^^^ */}


<HeaderOptions> <HeaderOptions>
<HeaderButtons>
{/* Setting display of offer cards to full width */}
<HeaderButton
iconColor={
props?.isGrid
? selectedTheme.colors.iconStrokeColor
: selectedTheme.colors.primaryPurple
}
onClick={() => props?.setIsGrid(false)}
>
<GridLine />
</HeaderButton>
{/* ^^^^^^ */}
{!props.hideGrid && (
<HeaderButtons>
{/* Setting display of offer cards to full width */}
<HeaderButton
iconColor={
props?.isGrid
? selectedTheme.colors.iconStrokeColor
: selectedTheme.colors.primaryPurple
}
onClick={() => props?.setIsGrid(false)}
>
<GridLine />
</HeaderButton>
{/* ^^^^^^ */}


{/* Setting display of offer cards to half width (Grid) */}
<HeaderButton
iconColor={
props?.isGrid
? selectedTheme.colors.primaryPurple
: selectedTheme.colors.iconStrokeColor
}
onClick={() => props?.setIsGrid(true)}
>
<GridSquare />
</HeaderButton>
{/* ^^^^^^ */}
</HeaderButtons>
{/* Setting display of offer cards to half width (Grid) */}
<HeaderButton
iconColor={
props?.isGrid
? selectedTheme.colors.primaryPurple
: selectedTheme.colors.iconStrokeColor
}
onClick={() => props?.setIsGrid(true)}
>
<GridSquare />
</HeaderButton>
{/* ^^^^^^ */}
</HeaderButtons>
)}


{/* Select option to choose sorting */} {/* Select option to choose sorting */}
<HeaderSelect <HeaderSelect
</HeaderContainer> </HeaderContainer>
{isMobile && ( {isMobile && (
<PageTitleContainer> <PageTitleContainer>
<SwapsIcon />
{props.users ? (
<UserIcon />
) : props.categories ? (
<CategoryIcon />
) : (
<SwapsIcon />
)}
<SwapsTitle> <SwapsTitle>
{props?.myOffers ? t("header.myOffers") : t("offer.offers")}
{props.users
? t("admin.users.headerTitle")
: props.categories
? t("admin.categories.headerTitle")
: props?.myOffers
? t("header.myOffers")
: t("offer.offers")}
</SwapsTitle> </SwapsTitle>
</PageTitleContainer> </PageTitleContainer>
)} )}
setIsGrid: PropTypes.func, setIsGrid: PropTypes.func,
isGrid: PropTypes.bool, isGrid: PropTypes.bool,
offers: PropTypes.any, offers: PropTypes.any,
category: PropTypes.string,
myOffers: PropTypes.bool, myOffers: PropTypes.bool,
skeleton: PropTypes.bool, skeleton: PropTypes.bool,
sorting: PropTypes.any, sorting: PropTypes.any,
isAdmin: PropTypes.bool, isAdmin: PropTypes.bool,
users: PropTypes.bool, users: PropTypes.bool,
categories: PropTypes.bool,
hideGrid: PropTypes.bool,
className: PropTypes.string,
hideBackButton: PropTypes.bool,
}; };
Header.defaultProps = { Header.defaultProps = {
isGrid: false, isGrid: false,

+ 13
- 0
src/components/MarketPlace/Header/Header.styled.js Просмотреть файл

import { ReactComponent as Swaps } from "../../../assets/images/svg/swaps.svg"; import { ReactComponent as Swaps } from "../../../assets/images/svg/swaps.svg";
import { ReactComponent as CategoryHeader } from "../../../assets/images/svg/category-header.svg"; import { ReactComponent as CategoryHeader } from "../../../assets/images/svg/category-header.svg";
import { ReactComponent as User } from "../../../assets/images/svg/user.svg"; import { ReactComponent as User } from "../../../assets/images/svg/user.svg";
import { ReactComponent as Category } from "../../../assets/images/svg/category.svg";


export const HeaderWrapperContainer = styled(Box)` export const HeaderWrapperContainer = styled(Box)`
display: ${(props) => (props.skeleton ? "none" : "block")}; display: ${(props) => (props.skeleton ? "none" : "block")};
top: 1px; top: 1px;
} }
`; `;
export const CategoryIcon = styled(Category)`
position: relative;
top: 4px;
right: 2px;
`;
export const PageTitleContainer = styled(Box)` export const PageTitleContainer = styled(Box)`
position: relative; position: relative;
left: 6px; left: 6px;
margin-top: 36px; margin-top: 36px;
width: 100px; width: 100px;
@media (max-width: 600px) {
& svg {
width: 12px;
height: 12px;
top: 2px;
}
}
`; `;
export const SwapsIcon = styled(RefreshIcon)` export const SwapsIcon = styled(RefreshIcon)`
width: 12px; width: 12px;

+ 17
- 4
src/components/MarketPlace/Offers/Offers.js Просмотреть файл

import { selectUserId } from "../../../store/selectors/loginSelectors"; import { selectUserId } from "../../../store/selectors/loginSelectors";
import { startChat } from "../../../util/helpers/chatHelper"; import { startChat } from "../../../util/helpers/chatHelper";
import OffersNotFound from "./OffersNotFound"; import OffersNotFound from "./OffersNotFound";
import HeadersMyOffers from "./HeaderMyOffers.js/HeadersMyOffers";
// import HeadersMyOffers from "./SearchBar/SearchBar";
import SkeletonOfferCard from "../../Cards/OfferCard/SkeletonOfferCard/SkeletonOfferCard"; import SkeletonOfferCard from "../../Cards/OfferCard/SkeletonOfferCard/SkeletonOfferCard";
import BigProfileCard from "../../Cards/ProfileCard/BigProfileCard/BigProfileCard"; import BigProfileCard from "../../Cards/ProfileCard/BigProfileCard/BigProfileCard";
import SearchField from "../../TextFields/SearchField/SearchField";
import { useTranslation } from "react-i18next";


const Offers = (props) => { const Offers = (props) => {
const chats = useSelector(selectLatestChats); const chats = useSelector(selectLatestChats);
const offersRef = useRef(null); const offersRef = useRef(null);
const userId = useSelector(selectUserId); const userId = useSelector(selectUserId);
const { t } = useTranslation();
const offers = props?.offers; const offers = props?.offers;
// For skeleton screen // For skeleton screen
const arrayForMapping = Array.apply(null, Array(4)).map(() => {}); const arrayForMapping = Array.apply(null, Array(4)).map(() => {});
<FilterIcon /> <FilterIcon />
</FilterContainer> </FilterContainer>
{(props?.myOffers || props?.isAdmin) && ( {(props?.myOffers || props?.isAdmin) && (
<HeadersMyOffers
// <HeadersMyOffers
<SearchField
searchMyOffers={offers?.search?.searchOffers} searchMyOffers={offers?.search?.searchOffers}
handleSearch={offers?.apply} handleSearch={offers?.apply}
isAdmin={props?.isAdmin} isAdmin={props?.isAdmin}
offers={offers} offers={offers}
isUsers={props.isUsers} isUsers={props.isUsers}
placeholder={
props.isUsers
? t("admin.users.searchPlaceholder")
: t("header.searchOffers")
}
/> />
)} )}
{offers?.allOffersToShow?.length === 0 ? ( {offers?.allOffersToShow?.length === 0 ? (
<OffersContainer ref={offersRef}> <OffersContainer ref={offersRef}>
{props.isUsers {props.isUsers
? props.users?.map((item) => ( ? props.users?.map((item) => (
<BigProfileCard key={item._id} profile={item} />
<BigProfileCard
key={item._id}
profile={item}
halfwidth={props?.isGrid}
/>
)) ))
: offers?.allOffersToShow?.map((item) => { : offers?.allOffersToShow?.map((item) => {
return ( return (


Offers.defaultProps = { Offers.defaultProps = {
myOffers: false, myOffers: false,
users: []
users: [],
}; };


export default Offers; export default Offers;

src/components/MarketPlace/Offers/HeaderMyOffers.js/HeadersMyOffers.js → src/components/TextFields/SearchField/SearchField.js Просмотреть файл

import React, { useCallback, useEffect, useRef } from "react"; import React, { useCallback, useEffect, useRef } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { EndIcon, SearchIcon, SearchInput } from "./HeadersMyOffers.styled";
import { useTranslation } from "react-i18next";
import useSearch from "../../../../hooks/useOffers/useSearch";
import { EndIcon, SearchIcon, SearchInput } from "./SearchField.styled";
import useSearch from "../../../hooks/useOffers/useSearch";


const HeadersMyOffers = (props) => {
const SearchField = (props) => {
const searchRef = useRef(null); const searchRef = useRef(null);
const search = useSearch(() => {}); const search = useSearch(() => {});
const { t } = useTranslation();
let listener = useCallback( let listener = useCallback(
(event) => { (event) => {
// Event keycode 13 = ENTER keycode // Event keycode 13 = ENTER keycode
const handleSearch = () => { const handleSearch = () => {
if (props.isAdmin) { if (props.isAdmin) {
console.log(searchRef.current.value); console.log(searchRef.current.value);
search.searchOffersImmediately(searchRef.current.value);
if (props.handleSearch) props.handleSearch(searchRef.current.value);
else search.searchOffersImmediately(searchRef.current.value);
} else { } else {
props.searchMyOffers(searchRef.current.value); props.searchMyOffers(searchRef.current.value);
props.handleSearch(); props.handleSearch();
return ( return (
<SearchInput <SearchInput
isAdmin={props.isAdmin} isAdmin={props.isAdmin}
fullWidth
fullWidth={props.fullWidth}
InputProps={{ InputProps={{
endAdornment: ( endAdornment: (
<EndIcon size="36px"> <EndIcon size="36px">
</EndIcon> </EndIcon>
), ),
}} }}
placeholder={
props.isUsers
? t("admin.users.searchPlaceholder")
: t("header.searchOffers")
}
placeholder={props.placeholder}
onFocus={handleFocusSearch} onFocus={handleFocusSearch}
onBlur={handleBlurSearch} onBlur={handleBlurSearch}
ref={searchRef} ref={searchRef}
className={props.className}
/> />
); );
}; };


HeadersMyOffers.propTypes = {
SearchField.propTypes = {
children: PropTypes.node, children: PropTypes.node,
searchMyOffers: PropTypes.func, searchMyOffers: PropTypes.func,
handleSearch: PropTypes.func, handleSearch: PropTypes.func,
isAdmin: PropTypes.bool, isAdmin: PropTypes.bool,
offers: PropTypes.any, offers: PropTypes.any,
isUsers: PropTypes.bool, isUsers: PropTypes.bool,
fullWidth: PropTypes.bool,
className: PropTypes.string,
placeholder: PropTypes.string,
};
SearchField.defaultProps = {
fullWidth: true,
}; };


export default HeadersMyOffers;
export default SearchField;

src/components/MarketPlace/Offers/HeaderMyOffers.js/HeadersMyOffers.styled.js → src/components/TextFields/SearchField/SearchField.styled.js Просмотреть файл

import { Box } from "@mui/material";
import styled from "styled-components"; import styled from "styled-components";
import { Icon } from "../../../Icon/Icon";
import { ReactComponent as Search } from "../../../../assets/images/svg/magnifying-glass.svg";
import selectedTheme from "../../../../themes";
import { TextField } from "../../../TextFields/TextField/TextField";
import selectedTheme from "../../../themes";
import { Icon } from "../../Icon/Icon";
import { TextField } from "../TextField/TextField";
import { ReactComponent as Search } from "../../../assets/images/svg/magnifying-glass.svg";



export const HeadersMyOffersContainer = styled(Box)``;
export const EndIcon = styled(Icon)``; export const EndIcon = styled(Icon)``;
export const SearchIcon = styled(Search)` export const SearchIcon = styled(Search)`
position: relative; position: relative;

+ 27
- 5
src/constants/adminNavigation.js Просмотреть файл

import { ReactComponent as CategoryIcon } from "../assets/images/svg/category.svg"; import { ReactComponent as CategoryIcon } from "../assets/images/svg/category.svg";
import { ReactComponent as LocationIcon } from "../assets/images/svg/location.svg"; import { ReactComponent as LocationIcon } from "../assets/images/svg/location.svg";
import { ReactComponent as DollarIcon } from "../assets/images/svg/dollar-sign.svg"; import { ReactComponent as DollarIcon } from "../assets/images/svg/dollar-sign.svg";
import { ADMIN_USERS_PAGE } from "./pages";
import {
ADMIN_CATEGORIES_PAGE,
ADMIN_LOCATIONS_PAGE,
ADMIN_PAYMENT_PAGE,
ADMIN_USERS_PAGE,
} from "./pages";
import i18n from "../i18n";


export const ADMIN_NAVIGATION = [ export const ADMIN_NAVIGATION = [
{ text: "Korisnici", icon: <UserIcon />, route: `${ADMIN_USERS_PAGE}` },
{ text: "Kategorije", icon: <CategoryIcon />, route: "/admin/categoires" },
{ text: "Lokacije", icon: <LocationIcon />, route: "/admin/locations" },
{ text: "Uplate", icon: <DollarIcon />, route: "/admin/payment" },
{
text: i18n.t("admin.navigation.users"),
icon: <UserIcon />,
route: `${ADMIN_USERS_PAGE}`,
},
{
text: i18n.t("admin.navigation.categories"),
icon: <CategoryIcon />,
route: `${ADMIN_CATEGORIES_PAGE}`,
},
{
text: i18n.t("admin.navigation.locations"),
icon: <LocationIcon />,
route: `${ADMIN_LOCATIONS_PAGE}`,
},
{
text: i18n.t("admin.navigation.payment"),
icon: <DollarIcon />,
route: `${ADMIN_PAYMENT_PAGE}`,
},
]; ];

+ 3
- 0
src/constants/pages.js Просмотреть файл

export const POLICY_PRIVACY_PAGE = "/policy"; export const POLICY_PRIVACY_PAGE = "/policy";
export const ADMIN_HOME_PAGE = "/admin"; export const ADMIN_HOME_PAGE = "/admin";
export const ADMIN_USERS_PAGE = "/admin/users"; export const ADMIN_USERS_PAGE = "/admin/users";
export const ADMIN_CATEGORIES_PAGE = "/admin/categories";
export const ADMIN_LOCATIONS_PAGE = "/admin/locations";
export const ADMIN_PAYMENT_PAGE = "/admin/payment";

+ 15
- 4
src/hooks/useOffers/useSearch.js Просмотреть файл

import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { BASE_PAGE, HOME_PAGE } from "../../constants/pages"; import { BASE_PAGE, HOME_PAGE } from "../../constants/pages";
import { KEY_SEARCH } from "../../constants/queryStringConstants"; import { KEY_SEARCH } from "../../constants/queryStringConstants";
import { setSearchString } from "../../store/actions/filters/filtersActions";
import { selectSearchString } from "../../store/selectors/filtersSelectors";
import {
setManualSearchString,
setSearchString,
} from "../../store/actions/filters/filtersActions";
import {
selectManualSearchString,
selectSearchString,
} from "../../store/selectors/filtersSelectors";
import { routeMatches } from "../../util/helpers/routeHelpers"; import { routeMatches } from "../../util/helpers/routeHelpers";


const useSearch = (applyAllFilters) => { const useSearch = (applyAllFilters) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const searchString = useSelector(selectSearchString); const searchString = useSelector(selectSearchString);
const history = useHistory(); const history = useHistory();
const manualSearchString = useSelector(selectManualSearchString);


// On every global change of search string, new request to backend should be sent // On every global change of search string, new request to backend should be sent
useEffect(() => { useEffect(() => {
}, [searchStringLocally]); }, [searchStringLocally]);


const searchOffers = (searchValue) => { const searchOffers = (searchValue) => {
console.log('searchoff')
setIsInitiallyLoaded(true); setIsInitiallyLoaded(true);
setSearchStringLocally(searchValue); setSearchStringLocally(searchValue);
}; };


const searchOffersImmediately = (searchValue) => { const searchOffersImmediately = (searchValue) => {
console.log('searchoffersimm');
setIsInitiallyLoaded(true); setIsInitiallyLoaded(true);
searchOffers(searchValue); searchOffers(searchValue);
applyAllFilters(); applyAllFilters();
}; };


const setSearchStringManually = (searchValue) => {
dispatch(setManualSearchString(searchValue));
};

const clear = () => { const clear = () => {
setSearchStringLocally(""); setSearchStringLocally("");
}; };
return { return {
searchOffers, searchOffers,
setSearchStringLocally, setSearchStringLocally,
setSearchStringManually,
searchOffersImmediately, searchOffersImmediately,
searchStringLocally, searchStringLocally,
searchString, searchString,
manualSearchString,
clear, clear,
}; };
}; };

+ 11
- 0
src/i18n/resources/rs.js Просмотреть файл

navigation: { navigation: {
role: "Administrator", role: "Administrator",
menu: "Meni", menu: "Meni",
users: "Korisnici",
categories: "Kategorije",
locations: "Lokacije",
payment: "Uplate",
logout: "Odjavi se", logout: "Odjavi se",
}, },
categories: {
checkCategory: "Pogledaj podkategorije",
noOfOffers: "Broj objava: ",
noOfSubcategories: "Broj potkategorija: ",
headerTitle: "Kategorije",
placeholder: "Pretražite kategorije...",
},
}, },
}; };

+ 71
- 0
src/pages/AdminCategoriesPage/AdminCategoriesPage.js Просмотреть файл

import React from "react";
import PropTypes from "prop-types";
import CategoryCard from "../../components/Cards/CategoryCard/CategoryCard";
import { useDispatch, useSelector } from "react-redux";
import { useEffect } from "react";
import { fetchCategories } from "../../store/actions/categories/categoriesActions";
import { selectCategories } from "../../store/selectors/categoriesSelectors";
import { useTranslation } from "react-i18next";
import {
AdminCategoriesHeader,
AdminCategoriesPageContainer,
AdminCategoriesSearchField,
} from "./AdminCategoriesPage.styled";
import { selectManualSearchString } from "../../store/selectors/filtersSelectors";
import { useMemo } from "react";
import { setManualSearchString } from "../../store/actions/filters/filtersActions";

const AdminCategoriesPage = () => {
const { t } = useTranslation();
const dispatch = useDispatch();
const categories = useSelector(selectCategories);
const manualSearchString = useSelector(selectManualSearchString);
useEffect(() => {
dispatch(fetchCategories());
}, []);
const handleSearch = (value) => {
console.log(value);
dispatch(setManualSearchString(value));
};
const categoriesToShow = useMemo(() => {
if (categories) {
console.log(categories);
console.log(manualSearchString);
if (manualSearchString)
return categories.filter((item) =>
item.name.toLowerCase().includes(manualSearchString.toLowerCase())
);
return categories;
}
return [];
}, [categories, manualSearchString]);
return (
<AdminCategoriesPageContainer>
<AdminCategoriesSearchField
isAdmin
handleSearch={handleSearch}
placeholder={t("admin.categories.placeholder")}
/>
<AdminCategoriesHeader
myOffers
categories
hideGrid
isAdmin
hideBackButton
/>
{categoriesToShow.map((category) => (
<CategoryCard
key={category._id}
category={category}
secondLabel={t("admin.categories.noOfSubcategories")}
/>
))}
</AdminCategoriesPageContainer>
);
};

AdminCategoriesPage.propTypes = {
children: PropTypes.node,
};

export default AdminCategoriesPage;

+ 32
- 0
src/pages/AdminCategoriesPage/AdminCategoriesPage.styled.js Просмотреть файл

import { Box } from "@mui/material";
import styled from "styled-components";
import Header from "../../components/MarketPlace/Header/Header";
import SearchField from "../../components/TextFields/SearchField/SearchField";

export const AdminCategoriesPageContainer = styled(Box)`
padding: 15px 60px 60px 60px;
@media (max-width: 600px) {
padding: 18px;
position: relative;
top: 65px;
}
`;
export const AdminCategoriesHeader = styled(Header)`
/* top: 40px; */
top: 0;
@media (max-width: 600px) {
top: -10px;
& div {
margin-top: 0;
}
& div div:nth-child(1) {
top: 22px;
}
}
`;
export const AdminCategoriesSearchField = styled(SearchField)`
top: -15px;
@media (max-width: 600px) {
display: none;
}
`;

+ 16
- 7
src/pages/AdminHomePage/AdminHomePage.js Просмотреть файл

// import { OFFERS_SCOPE } from "../../store/actions/offers/offersActionConstants"; // import { OFFERS_SCOPE } from "../../store/actions/offers/offersActionConstants";
import AdminUsersPage from "../AdminUsersPage/AdminUsersPage"; import AdminUsersPage from "../AdminUsersPage/AdminUsersPage";
import { selectMineProfile } from "../../store/selectors/profileSelectors"; import { selectMineProfile } from "../../store/selectors/profileSelectors";
import { Route, useHistory } from "react-router-dom";
import { ADMIN_USERS_PAGE, HOME_PAGE } from "../../constants/pages";
import { Switch, useHistory } from "react-router-dom";
import {
ADMIN_CATEGORIES_PAGE,
ADMIN_USERS_PAGE,
HOME_PAGE,
} from "../../constants/pages";
import { selectUserId } from "../../store/selectors/loginSelectors"; import { selectUserId } from "../../store/selectors/loginSelectors";
// import AdminRoute from "../../components/Router/AdminRoute";
import AdminCategoriesPage from "../AdminCategoriesPage/AdminCategoriesPage";
import AdminRoute from "../../components/Router/AdminRoute";


const AdminHomePage = () => { const AdminHomePage = () => {
const profile = useSelector(selectMineProfile); const profile = useSelector(selectMineProfile);
leftCard={<Sidebar />} leftCard={<Sidebar />}
content={ content={
// <MarketPlace offers={offers} isAdmin skeleton={isLoadingOffers} /> // <MarketPlace offers={offers} isAdmin skeleton={isLoadingOffers} />
<>
<Route path={ADMIN_USERS_PAGE} component={AdminUsersPage} />
<Route component={AdminUsersPage} />
</>
<Switch>
<AdminRoute path={ADMIN_USERS_PAGE} component={AdminUsersPage} />
<AdminRoute
path={ADMIN_CATEGORIES_PAGE}
component={AdminCategoriesPage}
/>
<AdminRoute component={AdminUsersPage} />
</Switch>
} }
/> />
); );

+ 5
- 2
src/store/actions/filters/filtersActionConstants.js Просмотреть файл

import { createClearType, createSetType } from "../actionHelpers"
import { createClearType, createSetType } from "../actionHelpers";


const FILTERS_SCOPE = "FILTERS"
const FILTERS_SCOPE = "FILTERS";
export const SET_FILTERS = createSetType(FILTERS_SCOPE); export const SET_FILTERS = createSetType(FILTERS_SCOPE);
export const CLEAR_FILTERS = createClearType(FILTERS_SCOPE); export const CLEAR_FILTERS = createClearType(FILTERS_SCOPE);
export const SET_CATEGORY = createSetType("FILTERS_SET_CATEGORY"); export const SET_CATEGORY = createSetType("FILTERS_SET_CATEGORY");
export const SET_QUERY_STRING = createSetType("FILTERS_SET_QUERY_STRING"); export const SET_QUERY_STRING = createSetType("FILTERS_SET_QUERY_STRING");
export const SET_HEADER_STRING = createSetType("FILTERS_SET_HEADER_STRING"); export const SET_HEADER_STRING = createSetType("FILTERS_SET_HEADER_STRING");
export const SET_SEARCH_STRING = createSetType("FILTERS_SET_SEARCH"); export const SET_SEARCH_STRING = createSetType("FILTERS_SET_SEARCH");
export const SET_MANUAL_SEARCH_STRING = createSetType(
"FILTERS_SET_MANUAL_SEARCH_STRING"
);

+ 46
- 30
src/store/actions/filters/filtersActions.js Просмотреть файл

import { CLEAR_FILTERS, SET_CATEGORY, SET_FILTERS, SET_HEADER_STRING, SET_IS_APPLIED, SET_LOCATIONS, SET_QUERY_STRING, SET_SEARCH_STRING, SET_SORT_OPTION, SET_SUBCATEGORY } from "./filtersActionConstants";
import {
CLEAR_FILTERS,
SET_CATEGORY,
SET_FILTERS,
SET_HEADER_STRING,
SET_IS_APPLIED,
SET_LOCATIONS,
SET_MANUAL_SEARCH_STRING,
SET_QUERY_STRING,
SET_SEARCH_STRING,
SET_SORT_OPTION,
SET_SUBCATEGORY,
} from "./filtersActionConstants";


export const setFilters = (payload) => ({ export const setFilters = (payload) => ({
type: SET_FILTERS,
payload,
})
type: SET_FILTERS,
payload,
});
export const clearFilters = () => ({ export const clearFilters = () => ({
type: CLEAR_FILTERS
})
type: CLEAR_FILTERS,
});
export const setFilteredCategory = (payload) => ({ export const setFilteredCategory = (payload) => ({
type: SET_CATEGORY,
payload
})
type: SET_CATEGORY,
payload,
});
export const setFilteredSubcategory = (payload) => ({ export const setFilteredSubcategory = (payload) => ({
type: SET_SUBCATEGORY,
payload
})
type: SET_SUBCATEGORY,
payload,
});
export const setManualSearchString = (payload) => ({
type: SET_MANUAL_SEARCH_STRING,
payload,
});
export const setFilteredLocations = (payload) => ({ export const setFilteredLocations = (payload) => ({
type: SET_LOCATIONS,
payload
})
type: SET_LOCATIONS,
payload,
});
export const setFilteredSortOption = (payload) => ({ export const setFilteredSortOption = (payload) => ({
type: SET_SORT_OPTION,
payload
})
type: SET_SORT_OPTION,
payload,
});
export const setIsAppliedStatus = (payload) => ({ export const setIsAppliedStatus = (payload) => ({
type: SET_IS_APPLIED,
payload,
})
type: SET_IS_APPLIED,
payload,
});
export const setQueryString = (payload) => ({ export const setQueryString = (payload) => ({
type: SET_QUERY_STRING,
payload,
})
type: SET_QUERY_STRING,
payload,
});
export const setHeaderString = (payload) => ({ export const setHeaderString = (payload) => ({
type: SET_HEADER_STRING,
payload
})
type: SET_HEADER_STRING,
payload,
});
export const setSearchString = (payload) => ({ export const setSearchString = (payload) => ({
type: SET_SEARCH_STRING,
payload
})
type: SET_SEARCH_STRING,
payload,
});

+ 29
- 16
src/store/reducers/filters/filtersReducer.js Просмотреть файл

SET_HEADER_STRING, SET_HEADER_STRING,
SET_IS_APPLIED, SET_IS_APPLIED,
SET_LOCATIONS, SET_LOCATIONS,
SET_MANUAL_SEARCH_STRING,
SET_SEARCH_STRING, SET_SEARCH_STRING,
SET_SORT_OPTION, SET_SORT_OPTION,
SET_SUBCATEGORY, SET_SUBCATEGORY,
categoryString: "", categoryString: "",
subcategoryString: "", subcategoryString: "",
locationsString: "", locationsString: "",
text: ""
text: "",
}, },
searchString: ""
searchString: "",
manualSearchString: "",
}, },
}; };


[SET_IS_APPLIED]: setIsAppliedStatus, [SET_IS_APPLIED]: setIsAppliedStatus,
[SET_HEADER_STRING]: setHeaderString, [SET_HEADER_STRING]: setHeaderString,
[SET_SEARCH_STRING]: setSearchString, [SET_SEARCH_STRING]: setSearchString,
[SET_MANUAL_SEARCH_STRING]: setManualSearchString,
}, },
initialState initialState
); );


function setManualSearchString(state, { payload }) {
return {
...state,
filters: {
...state.filters,
manualSearchString: payload,
},
};
}

function setFilters(state, { payload }) { function setFilters(state, { payload }) {
return { return {
...state, ...state,
}, },
}; };
} }
function setFilteredSortOption(state, {payload}) {
function setFilteredSortOption(state, { payload }) {
return { return {
...state, ...state,
filters: { filters: {
...state.filters, ...state.filters,
sortOption: payload, sortOption: payload,
}
}
},
};
} }
function setIsAppliedStatus(state, {payload}) {
function setIsAppliedStatus(state, { payload }) {
return { return {
...state, ...state,
filters: { filters: {
...state.filters, ...state.filters,
isApplied: payload, isApplied: payload,
}
}
},
};
} }
function setHeaderString(state, {payload}) {
function setHeaderString(state, { payload }) {
return { return {
...state, ...state,
filters: { filters: {
...state.filters, ...state.filters,
headerString: payload
}
}
headerString: payload,
},
};
} }
function setSearchString(state, {payload}) {
function setSearchString(state, { payload }) {
return { return {
...state, ...state,
filters: { filters: {
...state.filters, ...state.filters,
searchString: payload
}
}
searchString: payload,
},
};
} }

+ 15
- 11
src/store/selectors/filtersSelectors.js Просмотреть файл

import { createSelector } from 'reselect';
import { createSelector } from "reselect";


const filtersSelector = (state) => state.filters; const filtersSelector = (state) => state.filters;


export const selectFilters = createSelector( export const selectFilters = createSelector(
filtersSelector, filtersSelector,
(state) => state.filters,
(state) => state.filters
); );
export const selectSelectedCategory = createSelector( export const selectSelectedCategory = createSelector(
filtersSelector, filtersSelector,
(state => state.filters.category)
)
(state) => state.filters.category
);
export const selectSelectedSubcategory = createSelector( export const selectSelectedSubcategory = createSelector(
filtersSelector, filtersSelector,
(state) => state.filters.subcategory (state) => state.filters.subcategory
)
);
export const selectSelectedLocations = createSelector( export const selectSelectedLocations = createSelector(
filtersSelector, filtersSelector,
(state) => state.filters.locations (state) => state.filters.locations
)
);
export const selectSelectedSortOption = createSelector( export const selectSelectedSortOption = createSelector(
filtersSelector, filtersSelector,
(state) => state.filters.sortOption (state) => state.filters.sortOption
)
);
export const selectAppliedStatus = createSelector( export const selectAppliedStatus = createSelector(
filtersSelector, filtersSelector,
(state) => state.filters.isApplied (state) => state.filters.isApplied
)
);
export const selectHeaderString = createSelector( export const selectHeaderString = createSelector(
filtersSelector, filtersSelector,
(state) => state.filters.headerString (state) => state.filters.headerString
)
);
export const selectSearchString = createSelector( export const selectSearchString = createSelector(
filtersSelector, filtersSelector,
(state) => state.filters.searchString,
)
(state) => state.filters.searchString
);
export const selectManualSearchString = createSelector(
filtersSelector,
(state) => state.filters.manualSearchString
);

+ 11
- 0
src/util/helpers/colorHelper.js Просмотреть файл

export const hexToRGB = (hex, opacity) => {
var r = parseInt(hex.slice(1, 3), 16),
g = parseInt(hex.slice(3, 5), 16),
b = parseInt(hex.slice(5, 7), 16);

if (opacity) {
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
} else {
return `rgb(${r}, ${g}, ${b})`;
}
}

+ 3
- 1
src/util/helpers/routeHelpers.js Просмотреть файл

import { import {
ADMIN_CATEGORIES_PAGE,
ADMIN_HOME_PAGE, ADMIN_HOME_PAGE,
ADMIN_LOGIN_PAGE, ADMIN_LOGIN_PAGE,
ADMIN_USERS_PAGE, ADMIN_USERS_PAGE,
if ( if (
routeMatches(ADMIN_LOGIN_PAGE) || routeMatches(ADMIN_LOGIN_PAGE) ||
routeMatches(ADMIN_HOME_PAGE) || routeMatches(ADMIN_HOME_PAGE) ||
routeMatches(ADMIN_USERS_PAGE)
routeMatches(ADMIN_USERS_PAGE) ||
routeMatches(ADMIN_CATEGORIES_PAGE)
) )
return true; return true;
return false; return false;

Загрузка…
Отмена
Сохранить