Преглед изворни кода

Partly finished feature 688

feature/688
jovan.cirkovic пре 3 година
родитељ
комит
7c00f1e951
44 измењених фајлова са 807 додато и 114 уклоњено
  1. 5
    2
      src/components/Cards/CategoryCard/CategoryCard.js
  2. 15
    1
      src/components/Cards/CategoryCard/CategoryCardName/CategoryCardName.js
  3. 37
    3
      src/components/Cards/CategoryCard/CategoryCardName/CategoryCardName.styled.js
  4. 3
    0
      src/components/Cards/CategoryCard/CategoryCheckButton/CategoryCheckButton.styled.js
  5. 5
    0
      src/components/Cards/ItemDetailsCard/Information/Information.styled.js
  6. 1
    0
      src/components/Cards/ItemDetailsCard/ItemDetailsCard.styled.js
  7. 5
    0
      src/components/Cards/LabeledCard/LabeledCard.styled.js
  8. 5
    0
      src/components/Cards/OfferCard/OfferCard.styled.js
  9. 1
    0
      src/components/Cards/ProfileCard/BigProfileCard/BigProfileCard.js
  10. 6
    5
      src/components/Cards/ProfileCard/EditProfile/EditProfile.js
  11. 1
    0
      src/components/Cards/ProfileCard/ProfileCard.js
  12. 1
    13
      src/components/Cards/ProfileCard/SkeletonProfileCard/SkeletonProfileCard.styled.js
  13. 10
    0
      src/components/Dropdown/DropdownList/DropdownList.styled.js
  14. 1
    1
      src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/Category/CategoryDetail.js
  15. 7
    6
      src/components/MarketPlace/Header/Header.js
  16. 24
    0
      src/components/Modals/DeleteCategory/DeleteCategory.js
  17. 5
    0
      src/components/Modals/DeleteCategory/DeleteCategory.styled.js
  18. 33
    7
      src/components/Modals/EditCategory/EditCategory.js
  19. 3
    0
      src/constants/adminMethodConstants.js
  20. 7
    0
      src/constants/adminTypeConstants.js
  21. 22
    0
      src/enums/sortEnum.js
  22. 6
    1
      src/hooks/useOffers/useSorting.js
  23. 2
    1
      src/i18n/resources/rs.js
  24. 2
    2
      src/layouts/ItemDetailsLayout/ItemDetailsLayout.js
  25. 1
    1
      src/layouts/ItemDetailsLayout/ItemDetailsLayout.styled.js
  26. 11
    6
      src/pages/AdminHomePage/AdminCategoriesPage/AdminCategoriesPage.js
  27. 12
    6
      src/pages/AdminHomePage/AdminLocationsPage/AdminLocationsPage.js
  28. 29
    13
      src/pages/AdminHomePage/AdminSubcategoriesPage/AdminSubcategoriesPage.js
  29. 33
    4
      src/pages/AdminHomePage/AdminSubcategoriesPage/AdminSubcategoriesPage.styled.js
  30. 4
    3
      src/pages/AdminHomePage/AdminUsersPage/AdminUsersPage.js
  31. 21
    0
      src/request/apiEndpoints.js
  32. 48
    1
      src/request/categoriesRequest.js
  33. 25
    2
      src/request/locationsRequest.js
  34. 26
    2
      src/request/profileRequest.js
  35. 10
    0
      src/store/actions/admin/adminActionConstants.js
  36. 16
    0
      src/store/actions/admin/adminActions.js
  37. 10
    0
      src/store/actions/profile/profileActionConstants.js
  38. 32
    2
      src/store/actions/profile/profileActions.js
  39. 218
    0
      src/store/saga/adminSaga.js
  40. 3
    1
      src/store/saga/index.js
  41. 54
    29
      src/store/saga/profileSaga.js
  42. 42
    0
      src/util/helpers/adminSortHelper.js
  43. 3
    1
      src/util/helpers/imageUrlGetter.js
  44. 2
    1
      src/util/helpers/routeHelpers.js

+ 5
- 2
src/components/Cards/CategoryCard/CategoryCard.js Прегледај датотеку

const [openedDeleteModal, setOpenedDeleteModal] = useState(false); const [openedDeleteModal, setOpenedDeleteModal] = useState(false);
const [openedEditModal, setOpenedEditModal] = useState(false); const [openedEditModal, setOpenedEditModal] = useState(false);
const navigateToCategory = () => { const navigateToCategory = () => {
if (!props.subcategory) {
if (!props.hideCheckButton) {
history.push( history.push(
replaceInRoute(ADMIN_SUBCATEGORIES_PAGE, { replaceInRoute(ADMIN_SUBCATEGORIES_PAGE, {
categoryId: props.category._id, categoryId: props.category._id,
); );
} }
}; };
console.log(props);
return ( return (
<> <>
<CategoryCardContainer className={props.className}> <CategoryCardContainer className={props.className}>
<CategoryCardLeftContainer> <CategoryCardLeftContainer>
<CategoryCardName <CategoryCardName
image={props?.category?.image}
categoryName={props?.category?.name || props?.category?.city} categoryName={props?.category?.name || props?.category?.city}
onClick={navigateToCategory} onClick={navigateToCategory}
/> />
</CategoryCardContainer> </CategoryCardContainer>
{openedDeleteModal && ( {openedDeleteModal && (
<DeleteCategory <DeleteCategory
categoryId={props.categoryId}
setOpenedDeleteModal={setOpenedDeleteModal} setOpenedDeleteModal={setOpenedDeleteModal}
subcategory={props.subcategory} subcategory={props.subcategory}
category={props.category} category={props.category}
hideImagePicker={props.type !== "categories"} hideImagePicker={props.type !== "categories"}
setOpenedEditModal={setOpenedEditModal} setOpenedEditModal={setOpenedEditModal}
category={props.category} category={props.category}
categoryId={props.categoryId}
subcategory={props.subcategory} subcategory={props.subcategory}
type={props.type} type={props.type}
method="edit" method="edit"
className: PropTypes.string, className: PropTypes.string,
subcategory: PropTypes.bool, subcategory: PropTypes.bool,
type: PropTypes.string, type: PropTypes.string,
categoryId: PropTypes.string,
}; };


export default CategoryCard; export default CategoryCard;

+ 15
- 1
src/components/Cards/CategoryCard/CategoryCardName/CategoryCardName.js Прегледај датотеку

import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { import {
CategoryCardImage,
CategoryCardImageContainer,
CategoryCardNameContainer, CategoryCardNameContainer,
CategoryCardNameText, CategoryCardNameText,
CategoryCardNameTextContainer,
} from "./CategoryCardName.styled"; } from "./CategoryCardName.styled";
import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter";


const CategoryCardName = (props) => { const CategoryCardName = (props) => {
return ( return (
<CategoryCardNameContainer onClick={props.onClick}> <CategoryCardNameContainer onClick={props.onClick}>
<CategoryCardNameText>{props.categoryName}</CategoryCardNameText>
{props.image && (
<CategoryCardImageContainer>
<CategoryCardImage
src={getImageUrl(props.image, variants.categoryIcon)}
/>
</CategoryCardImageContainer>
)}
<CategoryCardNameTextContainer>
<CategoryCardNameText>{props.categoryName}</CategoryCardNameText>
</CategoryCardNameTextContainer>
</CategoryCardNameContainer> </CategoryCardNameContainer>
); );
}; };
children: PropTypes.node, children: PropTypes.node,
categoryName: PropTypes.string, categoryName: PropTypes.string,
onClick: PropTypes.func, onClick: PropTypes.func,
image: PropTypes.string,
}; };


export default CategoryCardName; export default CategoryCardName;

+ 37
- 3
src/components/Cards/CategoryCard/CategoryCardName/CategoryCardName.styled.js Прегледај датотеку

import selectedTheme from "../../../../themes"; import selectedTheme from "../../../../themes";


export const CategoryCardNameContainer = styled(Box)` export const CategoryCardNameContainer = styled(Box)`
padding: 18px;
min-width: 234px;
width: 234px;
max-width: 234px;
display: flex;
padding-left: 18px;
/* max-width: 300px; */
flex-direction: row;
`; `;
export const CategoryCardNameTextContainer = styled(Box)`
height: 82px;
display: flex;
flex-direction: column;
justify-content: center;
@media (max-width: 600px) {
height: 60px;
}
`
export const CategoryCardNameText = styled(Typography)` export const CategoryCardNameText = styled(Typography)`
font-family: ${selectedTheme.fonts.textFont}; font-family: ${selectedTheme.fonts.textFont};
font-weight: 700; font-weight: 700;
font-size: 16px; font-size: 16px;
line-height: 21px; line-height: 21px;
padding-top: 12px;
max-height: 42px;
overflow: hidden;
text-overflow: ellipsis;
color: ${selectedTheme.colors.primaryPurple}; color: ${selectedTheme.colors.primaryPurple};
cursor: pointer; cursor: pointer;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
@media (max-width: 600px) { @media (max-width: 600px) {
font-size: 18px; font-size: 18px;
padding: 0; padding: 0;
} }
`; `;
export const CategoryCardImageContainer = styled(Box)`
height: 82px;
display: flex;
flex-direction: column;
justify-content: center;
@media (max-width: 600px) {
height: 60px;
}
`
export const CategoryCardImage = styled.img`
width: 18px;
height: 18px;
margin-right: 9px;
position: relative;
top: 1px;
`;

+ 3
- 0
src/components/Cards/CategoryCard/CategoryCheckButton/CategoryCheckButton.styled.js Прегледај датотеку

background-color: ${selectedTheme.colors.primaryPurple} !important; background-color: ${selectedTheme.colors.primaryPurple} !important;
color: white !important; color: white !important;
} }
@media (max-width: 1390px) {
display: none;
}
@media (max-width: 850px) { @media (max-width: 850px) {
display: none; display: none;
} }

+ 5
- 0
src/components/Cards/ItemDetailsCard/Information/Information.styled.js Прегледај датотеку

text-transform: capitalize; text-transform: capitalize;
color: ${selectedTheme.colors.primaryText}; color: ${selectedTheme.colors.primaryText};
font-size: 12px; font-size: 12px;
max-width: 250px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-height: 16px;
@media (max-width: 600px) { @media (max-width: 600px) {
font-size: 12px; font-size: 12px;
} }

+ 1
- 0
src/components/Cards/ItemDetailsCard/ItemDetailsCard.styled.js Прегледај датотеку

padding: 18px; padding: 18px;
max-width: 2000px; max-width: 2000px;
position: relative; position: relative;
width: auto;
${(props) => !props.previewCard && `height: 654px;`} ${(props) => !props.previewCard && `height: 654px;`}
/* height: 654px; */ /* height: 654px; */
/* padding-bottom: 70px; */ /* padding-bottom: 70px; */

+ 5
- 0
src/components/Cards/LabeledCard/LabeledCard.styled.js Прегледај датотеку

position: relative; position: relative;
width: ${(props) => props.width || `min-content`}; width: ${(props) => props.width || `min-content`};
height: ${(props) => props.height || `57px`}; height: ${(props) => props.height || `57px`};
/* max-width: 350px;
overflow: hidden;
white-space: nowrap;
display: inline-block;
text-overflow: ellipsis; */
padding: 18px; padding: 18px;
`; `;
export const LabeledCardIconContainer = styled(Box)` export const LabeledCardIconContainer = styled(Box)`

+ 5
- 0
src/components/Cards/OfferCard/OfferCard.styled.js Прегледај датотеку

line-height: 16px; line-height: 16px;
font-size: 12px; font-size: 12px;
position: relative; position: relative;
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-height: 16px;
top: -2px; top: -2px;
left: 3px; left: 3px;
`; `;

+ 1
- 0
src/components/Cards/ProfileCard/BigProfileCard/BigProfileCard.js Прегледај датотеку

)} )}
{deleteOrEditModal.show && ( {deleteOrEditModal.show && (
<DeleteCategory <DeleteCategory
customId={props.profile._id}
setOpenedDeleteModal={closeModalHandler} setOpenedDeleteModal={closeModalHandler}
type={deleteOrEditModal.type} type={deleteOrEditModal.type}
customLabeledCard={<UserLabeledCard user={props.profile} />} customLabeledCard={<UserLabeledCard user={props.profile} />}

+ 6
- 5
src/components/Cards/ProfileCard/EditProfile/EditProfile.js Прегледај датотеку

import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
editProfile, editProfile,
fetchAllProfiles,
editProfileAsAdmin,
fetchAllProfilesAsAdmin,
fetchMineProfile, fetchMineProfile,
} from "../../../../store/actions/profile/profileActions"; } from "../../../../store/actions/profile/profileActions";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
const handleApiResponseSuccess = () => { const handleApiResponseSuccess = () => {
if (dynamicRouteMatches(PROFILE_PAGE)) dispatch(fetchMineProfile(userId)); if (dynamicRouteMatches(PROFILE_PAGE)) dispatch(fetchMineProfile(userId));
if (routeMatches(ADMIN_USERS_PAGE) || routeMatches(ADMIN_HOME_PAGE)) if (routeMatches(ADMIN_USERS_PAGE) || routeMatches(ADMIN_HOME_PAGE))
dispatch(fetchAllProfiles());
dispatch(fetchAllProfilesAsAdmin());
props.reFetchProfile(); props.reFetchProfile();
}; };


const handleSubmit = (values) => { const handleSubmit = (values) => {
if (props.isAdmin) { if (props.isAdmin) {
dispatch( dispatch(
editProfile({
editProfileAsAdmin({
userId: props.userId, userId: props.userId,
...values, ...values,
firmLogo: profileImage,
firmLogo: profileImage === props.profile.image ? null : profileImage,
handleApiResponseSuccess, handleApiResponseSuccess,
}) })
); );
dispatch( dispatch(
editProfile({ editProfile({
...values, ...values,
firmLogo: profileImage,
firmLogo: profileImage === props.profile.image ? null : profileImage,
handleApiResponseSuccess, handleApiResponseSuccess,
}) })
); );

+ 1
- 0
src/components/Cards/ProfileCard/ProfileCard.js Прегледај датотеку

{editProfileModal && ( {editProfileModal && (
<EditProfile <EditProfile
profile={profile} profile={profile}
isAdmin={props.isAdmin}
closeModalHandler={closeModalHandler} closeModalHandler={closeModalHandler}
reFetchProfile={reFetchProfile} reFetchProfile={reFetchProfile}
/> />

+ 1
- 13
src/components/Cards/ProfileCard/SkeletonProfileCard/SkeletonProfileCard.styled.js Прегледај датотеку

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

const skeletonAnimation = keyframes`
0% {
opacity: 1;
}
50% {
opacity: 0.63;
}
100% {
opacity: 1
}
`;
const skeletonBackgroundAnimation = keyframes` const skeletonBackgroundAnimation = keyframes`
0% { 0% {
opacity: 1; opacity: 1;


export const SkeletonItemBackgroundColor = styled(Box)` export const SkeletonItemBackgroundColor = styled(Box)`
background-color: ${selectedTheme.colors.filterSkeletonItems}; background-color: ${selectedTheme.colors.filterSkeletonItems};
animation: ${skeletonAnimation} 1.2s infinite;
animation: ${skeletonBackgroundAnimation} 1.2s infinite;
`; `;
export const SkeletonBackgroundColor = styled(Box)` export const SkeletonBackgroundColor = styled(Box)`
background-color: ${selectedTheme.colors.filterSkeletonBackground}; background-color: ${selectedTheme.colors.filterSkeletonBackground};

+ 10
- 0
src/components/Dropdown/DropdownList/DropdownList.styled.js Прегледај датотеку

padding-bottom: 10px; padding-bottom: 10px;
padding-top: 5px; padding-top: 5px;
padding-right: 0.9rem; padding-right: 0.9rem;
width: 185px;
margin-right: 15px;
max-width: 185px;
overflow: hidden;
white-space: nowrap;
display: inline-block;
text-overflow: ellipsis;
max-height: 26px;
font-family: ${selectedTheme.fonts.textFont}; font-family: ${selectedTheme.fonts.textFont};
color: ${(props) => color: ${(props) =>
props.disabled props.disabled
: selectedTheme.coloros.primaryText}; : selectedTheme.coloros.primaryText};
@media (max-width: 600px) { @media (max-width: 600px) {
font-size: 14px; font-size: 14px;
width: 260px;
max-width: 260px;
} }
`; `;



+ 1
- 1
src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/Category/CategoryDetail.js Прегледај датотеку

<LocationIcon /> <LocationIcon />
</DetailIcon> </DetailIcon>
<DetailText ismyprofile={props.isMyProfile}> <DetailText ismyprofile={props.isMyProfile}>
{offer.offer.location.city}
{offer.offer?.location?.city}
</DetailText> </DetailText>
</DetailContainer> </DetailContainer>
); );

+ 7
- 6
src/components/MarketPlace/Header/Header.js Прегледај датотеку

const sorting = props?.sorting; const sorting = props?.sorting;
const headerString = useSelector(selectHeaderString); const headerString = useSelector(selectHeaderString);
const { isMobile } = useIsMobile(); const { isMobile } = useIsMobile();
// Changing header string on refresh or on load


// Changing header string on refresh or on load
const altString = useMemo(() => { const altString = useMemo(() => {
if (!props?.users) { if (!props?.users) {
if (sorting?.selectedSortOption === sortEnum.OLD) { if (sorting?.selectedSortOption === sortEnum.OLD) {
}); });


const handleChangeSelect = (event) => { const handleChangeSelect = (event) => {
console.log(sorting);
if (!props.users) sorting?.changeSorting(event.target.value); if (!props.users) sorting?.changeSorting(event.target.value);
}; };
const handleClickCategory = () => { const handleClickCategory = () => {
<SelectOption style={{ display: "none" }} value="default"> <SelectOption style={{ display: "none" }} value="default">
{t("reviews.sortBy")} {t("reviews.sortBy")}
</SelectOption> </SelectOption>
{Object.keys(sortEnum).map((property) => {
if (sortEnum[property].value === 0) return;
{Object.keys(sorting?.sortOptions).map((property) => {
if (sorting?.sortOptions[property].value === 0) return;
return ( return (
<SelectOption <SelectOption
value={sortEnum[property]}
key={sortEnum[property].value}
value={sorting?.sortOptions[property]}
key={sorting?.sortOptions[property].value}
> >
{sortEnum[property].mainText}
{sorting?.sortOptions[property].mainText}
</SelectOption> </SelectOption>
); );
})} })}

+ 24
- 0
src/components/Modals/DeleteCategory/DeleteCategory.js Прегледај датотеку

import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useMemo } from "react"; import { useMemo } from "react";
import selectedTheme from "../../../themes"; import selectedTheme from "../../../themes";
import { useDispatch } from "react-redux";
import { fetchAdminMethod } from "../../../store/actions/admin/adminActions";
import { DELETE_TYPE } from "../../../constants/adminMethodConstants";


const DeleteCategory = (props) => { const DeleteCategory = (props) => {
const dispatch = useDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
console.log(props.category); console.log(props.category);
const handleCancel = () => { const handleCancel = () => {
const reassuranceText = useMemo(() => { const reassuranceText = useMemo(() => {
return t(`admin.${props.type}.reassuranceDelete`); return t(`admin.${props.type}.reassuranceDelete`);
}, [props, t]); }, [props, t]);

const handleApiResponseSuccess = () => {
handleCancel();
};

const handleSubmit = () => {
dispatch(
fetchAdminMethod({
type: props.type,
method: DELETE_TYPE,
name: props.category?.name,
id: props.customId || props.category?._id,
categoryId: props.categoryId,
handleApiResponseSuccess,
})
);
};
return ( return (
<> <>
<BackdropComponent <BackdropComponent
<SaveButton <SaveButton
variant="outlined" variant="outlined"
buttoncolor={selectedTheme.colors.primaryPurple} buttoncolor={selectedTheme.colors.primaryPurple}
onClick={handleSubmit}
> >
{t(`admin.${props.type}.delete`)} {t(`admin.${props.type}.delete`)}
</SaveButton> </SaveButton>
customLabeledCardWidth: PropTypes.string, customLabeledCardWidth: PropTypes.string,
customLabeledCardHeight: PropTypes.string, customLabeledCardHeight: PropTypes.string,
customLabeledCardIcon: PropTypes.node, customLabeledCardIcon: PropTypes.node,
categoryId: PropTypes.string,
customId: PropTypes.string,
}; };


export default DeleteCategory; export default DeleteCategory;

+ 5
- 0
src/components/Modals/DeleteCategory/DeleteCategory.styled.js Прегледај датотеку

font-size: 16px; font-size: 16px;
white-space: nowrap; white-space: nowrap;
line-height: 21px; line-height: 21px;
max-width: 350px;
overflow: hidden;
white-space: nowrap;
display: inline-block;
text-overflow: ellipsis;
color: ${selectedTheme.colors.primaryPurple}; color: ${selectedTheme.colors.primaryPurple};
`; `;
export const ReassuranceText = styled(Typography)` export const ReassuranceText = styled(Typography)`

+ 33
- 7
src/components/Modals/EditCategory/EditCategory.js Прегледај датотеку

import { Trans, useTranslation } from "react-i18next"; import { Trans, useTranslation } from "react-i18next";
import { useFormik } from "formik"; import { useFormik } from "formik";
import { useMemo } from "react"; import { useMemo } from "react";
import { useDispatch } from "react-redux";
import { fetchAdminMethod } from "../../../store/actions/admin/adminActions";
import { useRef } from "react";


const EditCategory = (props) => { const EditCategory = (props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [image, setImage] = useState("");
console.log(props);
const dispatch = useDispatch();
const [clickedOnNext, setClickedOnNext] = useState(false);
const setImage = useState("")[1];
const inputRef = useRef(null);
const title = useMemo(() => { const title = useMemo(() => {
return t(`admin.${props.type}.${props.method}.title`); return t(`admin.${props.type}.${props.method}.title`);
}, [props.type, props.method]); }, [props.type, props.method]);
setImage(image); setImage(image);
formik.setFieldValue("image", image); formik.setFieldValue("image", image);
}; };
const handleApiResponseSuccess = () => {
if (clickedOnNext) {
formik.resetForm();
inputRef.current.focus();
} else props.setOpenedEditModal(false);
};
const handleSubmit = (values) => { const handleSubmit = (values) => {
console.log(values);
console.log(image);
dispatch(
fetchAdminMethod({
type: props.type,
method: props.method,
values,
name: props?.category?.name,
id: props?.category?._id,
categoryId: props.categoryId,
handleApiResponseSuccess,
})
);
}; };
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
image: "", image: "",
title: props?.category?.name || props?.category?.city || "",
title: props?.category?.name || props?.category?.city || "",
}, },
onSubmit: handleSubmit, onSubmit: handleSubmit,
}); });
const handleClick = (next) => {
if (next !== clickedOnNext) setClickedOnNext(next);
formik.handleSubmit();
};
console.log(props); console.log(props);
return ( return (
<> <>
<FieldLabel leftText={fieldLabel} /> <FieldLabel leftText={fieldLabel} />
<CategoryTitleField <CategoryTitleField
name="title" name="title"
ref={inputRef}
placeholder={placeholder} placeholder={placeholder}
italicPlaceholder italicPlaceholder
margin="normal" margin="normal"
showSecondButton={props.showSecondButton} showSecondButton={props.showSecondButton}
variant={props.firstOutlined ? "outlined" : "contained"} variant={props.firstOutlined ? "outlined" : "contained"}
height="48px" height="48px"
onClick={formik.handleSubmit}
onClick={() => handleClick(true)}
> >
{firstButtonText} {firstButtonText}
</SaveButton> </SaveButton>
<SaveButton <SaveButton
variant={props.secondOutlined ? "outlined" : "contained"} variant={props.secondOutlined ? "outlined" : "contained"}
showSecondButton={props.showSecondButton} showSecondButton={props.showSecondButton}
onClick={formik.handleSubmit}
onClick={() => handleClick(false)}
> >
{secondButtonText} {secondButtonText}
</SaveButton> </SaveButton>
method: PropTypes.string, method: PropTypes.string,
firstOutlined: PropTypes.bool, firstOutlined: PropTypes.bool,
secondOutlined: PropTypes.bool, secondOutlined: PropTypes.bool,
categoryId: PropTypes.string,
}; };


EditCategory.defaultProps = { EditCategory.defaultProps = {

+ 3
- 0
src/constants/adminMethodConstants.js Прегледај датотеку

export const EDIT_TYPE = "edit";
export const DELETE_TYPE = "delete";
export const ADD_TYPE = "add";

+ 7
- 0
src/constants/adminTypeConstants.js Прегледај датотеку

export const CATEGORIES_TYPE = "categories";
export const SUBCATEGORIES_TYPE = "subcategories";
export const USERS_TYPE = "users";
export const LOCATIONS_TYPE = "locations";
export const REVIEW_TYPE = "reviews";
export const USERS_BLOCK_TYPE = "blockUser";
export const USERS_DELETE_TYPE = "deleteUser"

+ 22
- 0
src/enums/sortEnum.js Прегледај датотеку

value: 2, value: 2,
mainText: "Dobijene" mainText: "Dobijene"
} }
}
export const sortCategoriesEnum = {
INITIAL: {
value: 0,
mainText: "Sortiraj po",
queryString: ""
},
POPULAR: {
value: 1,
mainText: "Najpopularnije",
queryString: "popular"
},
NEW: {
value: 2,
mainText: "Najskorije dodate",
queryString: "newest"
},
OLD: {
value: 3,
mainText: "Najstarije dodate",
queryString: "oldest"
}
} }

+ 6
- 1
src/hooks/useOffers/useSorting.js Прегледај датотеку

import { useMemo } from "react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { import {
import { setFilteredSortOption } from "../../store/actions/filters/filtersActions"; import { setFilteredSortOption } from "../../store/actions/filters/filtersActions";
import { selectSelectedSortOption } from "../../store/selectors/filtersSelectors"; import { selectSelectedSortOption } from "../../store/selectors/filtersSelectors";


const useSorting = (applyAllFilters) => {
const useSorting = (applyAllFilters, newSortOptions = sortEnum) => {
const selectedSortOption = useSelector(selectSelectedSortOption); const selectedSortOption = useSelector(selectSelectedSortOption);
const [selectedSortOptionLocally, setSelectedSortOptionLocally] = useState( const [selectedSortOptionLocally, setSelectedSortOptionLocally] = useState(
sortEnum.INITIAL sortEnum.INITIAL
); );
const [isInitiallyLoaded, setIsInitallyLoaded] = useState(false); const [isInitiallyLoaded, setIsInitallyLoaded] = useState(false);
const dispatch = useDispatch(); const dispatch = useDispatch();
const sortOptions = useMemo(() => {
return newSortOptions || sortEnum;
}, [newSortOptions])


// On every change of sorting option, new request to backend should be sent // On every change of sorting option, new request to backend should be sent
useEffect(() => { useEffect(() => {
changeSortingFromName, changeSortingFromName,
apply, apply,
clear, clear,
sortOptions
}; };
}; };
export default useSorting; export default useSorting;

+ 2
- 1
src/i18n/resources/rs.js Прегледај датотеку

addSubcategory: "Dodaj podkategoriju", addSubcategory: "Dodaj podkategoriju",
cancel: "Otkaži", cancel: "Otkaži",
delete: "Obriši", delete: "Obriši",
goBack: "Nazad na sve kategorije",
reassuranceDelete: reassuranceDelete:
"Da li ste sigurni da želite da obrišete odabranu podkategoriju?", "Da li ste sigurni da želite da obrišete odabranu podkategoriju?",
edit: { edit: {
fieldTitle: "Naslov", fieldTitle: "Naslov",
placeholder: "Naziv podkategorije...", placeholder: "Naziv podkategorije...",
save: "Izmeni", save: "Izmeni",
next: "Sledeća",
next: "Izmeni",
}, },
add: { add: {
title: "Nova Podkategorija", title: "Nova Podkategorija",

+ 2
- 2
src/layouts/ItemDetailsLayout/ItemDetailsLayout.js Прегледај датотеку

> >
{props.children} {props.children}
<ContentRightCardContainer> <ContentRightCardContainer>
<Content item>{props.content}</Content>
<RightCard item singleOffer={props.singleOffer} profile={props.profile}>
<Content lg={5} item>{props.content}</Content>
<RightCard lg={7} item singleOffer={props.singleOffer} profile={props.profile}>
{props.rightCard} {props.rightCard}
</RightCard> </RightCard>
</ContentRightCardContainer> </ContentRightCardContainer>

+ 1
- 1
src/layouts/ItemDetailsLayout/ItemDetailsLayout.styled.js Прегледај датотеку

/* padding-left: 36px; */ /* padding-left: 36px; */
/* padding-right: ${(props) => (props.singleOffer ? "76px" : 0)}; */ /* padding-right: ${(props) => (props.singleOffer ? "76px" : 0)}; */
margin: 0; margin: 0;
width: 100vw;
width: calc(100vw - 17px);
max-width: 100vw; max-width: 100vw;
/* display: flex; */ /* display: flex; */
position: relative; position: relative;

+ 11
- 6
src/pages/AdminHomePage/AdminCategoriesPage/AdminCategoriesPage.js Прегледај датотеку

import selectedTheme from "../../../themes"; import selectedTheme from "../../../themes";
import { useState } from "react"; import { useState } from "react";
import EditCategory from "../../../components/Modals/EditCategory/EditCategory"; import EditCategory from "../../../components/Modals/EditCategory/EditCategory";
import useSorting from "../../../hooks/useOffers/useSorting";
import { sortCategoriesEnum } from "../../../enums/sortEnum";
import { adminSortMethod } from "../../../util/helpers/adminSortHelper";


const AdminCategoriesPage = () => { const AdminCategoriesPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useDispatch(); const dispatch = useDispatch();
const sorting = useSorting(() => {}, sortCategoriesEnum);
const categories = useSelector(selectCategories); const categories = useSelector(selectCategories);
const manualSearchString = useSelector(selectManualSearchString); const manualSearchString = useSelector(selectManualSearchString);
const [openedAddModal, setOpenedAddModal] = useState(false); const [openedAddModal, setOpenedAddModal] = useState(false);
useEffect(() => { useEffect(() => {
dispatch(fetchCategories()); dispatch(fetchCategories());
return () => {
dispatch(setManualSearchString(""));
sorting.clear();
};
}, []); }, []);
const handleSearch = (value) => { const handleSearch = (value) => {
console.log(value); console.log(value);
}; };
const categoriesToShow = useMemo(() => { const categoriesToShow = useMemo(() => {
if (categories) { if (categories) {
if (manualSearchString)
return categories.filter((item) =>
item.name.toLowerCase().includes(manualSearchString.toLowerCase())
);
return categories;
return adminSortMethod(categories, manualSearchString, sorting);
} }
return []; return [];
}, [categories, manualSearchString]);
}, [categories, manualSearchString, sorting.selectedSortOptionLocally]);
return ( return (
<> <>
<AdminCategoriesPageContainer> <AdminCategoriesPageContainer>
categories categories
hideGrid hideGrid
isAdmin isAdmin
sorting={sorting}
hideBackButton hideBackButton
/> />
<CategoriesList> <CategoriesList>

+ 12
- 6
src/pages/AdminHomePage/AdminLocationsPage/AdminLocationsPage.js Прегледај датотеку

import EditCategory from "../../../components/Modals/EditCategory/EditCategory"; import EditCategory from "../../../components/Modals/EditCategory/EditCategory";
import { setManualSearchString } from "../../../store/actions/filters/filtersActions"; import { setManualSearchString } from "../../../store/actions/filters/filtersActions";
import { selectManualSearchString } from "../../../store/selectors/filtersSelectors"; import { selectManualSearchString } from "../../../store/selectors/filtersSelectors";
import useSorting from "../../../hooks/useOffers/useSorting";
import { sortCategoriesEnum } from "../../../enums/sortEnum";
import { adminSortMethod } from "../../../util/helpers/adminSortHelper";


const AdminLocationsPage = () => { const AdminLocationsPage = () => {
const [openedAddModal, setOpenedAddModal] = useState(false); const [openedAddModal, setOpenedAddModal] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useDispatch(); const dispatch = useDispatch();
const sorting = useSorting(() => {}, sortCategoriesEnum);
const locations = useSelector(selectLocations); const locations = useSelector(selectLocations);
const manualSearchString = useSelector(selectManualSearchString); const manualSearchString = useSelector(selectManualSearchString);
useEffect(() => { useEffect(() => {
dispatch(fetchLocations()); dispatch(fetchLocations());
return () => {
dispatch(setManualSearchString(""));
sorting.clear();
};
}, []); }, []);
const handleSearch = (value) => { const handleSearch = (value) => {
console.log(value); console.log(value);
}; };
const locationsToShow = useMemo(() => { const locationsToShow = useMemo(() => {
if (locations) { if (locations) {
if (manualSearchString)
return locations.filter((item) =>
item.city.toLowerCase().includes(manualSearchString.toLowerCase())
);
return locations;
return adminSortMethod(locations, manualSearchString, sorting);
} }
}, [locations, manualSearchString]);
}, [locations, manualSearchString, sorting.selectedSortOptionLocally]);
return ( return (
<> <>
<AdminLocationsPageContainer> <AdminLocationsPageContainer>
hideGrid hideGrid
isAdmin isAdmin
hideBackButton hideBackButton
sorting={sorting}
/> />
<LocationsList> <LocationsList>
{locationsToShow.map((category) => ( {locationsToShow.map((category) => (
<CategoryCard <CategoryCard
dontNavigate
key={category._id} key={category._id}
category={category} category={category}
type="locations" type="locations"

+ 29
- 13
src/pages/AdminHomePage/AdminSubcategoriesPage/AdminSubcategoriesPage.js Прегледај датотеку

AdminSubcategoriesHeader, AdminSubcategoriesHeader,
AdminSubcategoriesPageContainer, AdminSubcategoriesPageContainer,
AdminSubcategoriesSearchField, AdminSubcategoriesSearchField,
ButtonContainer,
HeaderText,
NewSubcategoryButton, NewSubcategoryButton,
SponsoredCategoryCard, SponsoredCategoryCard,
SubcategoriesList, SubcategoriesList,
import selectedTheme from "../../../themes"; import selectedTheme from "../../../themes";
import { useState } from "react"; import { useState } from "react";
import EditCategory from "../../../components/Modals/EditCategory/EditCategory"; import EditCategory from "../../../components/Modals/EditCategory/EditCategory";
import useSorting from "../../../hooks/useOffers/useSorting";
import { sortCategoriesEnum } from "../../../enums/sortEnum";
import { adminSortMethod } from "../../../util/helpers/adminSortHelper";
import { ArrowButton } from "../../../components/Buttons/ArrowButton/ArrowButton";
import history from "../../../store/utils/history";


const AdminSubcategoriesPage = () => { const AdminSubcategoriesPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useDispatch(); const dispatch = useDispatch();
const categories = useSelector(selectCategories); const categories = useSelector(selectCategories);
const routeMatch = useRouteMatch(); const routeMatch = useRouteMatch();
const sorting = useSorting(() => {}, sortCategoriesEnum);
const manualSearchString = useSelector(selectManualSearchString); const manualSearchString = useSelector(selectManualSearchString);
const [openedAddModal, setOpenedAddModal] = useState(false); const [openedAddModal, setOpenedAddModal] = useState(false);


useEffect(() => { useEffect(() => {
dispatch(fetchCategories()); dispatch(fetchCategories());
return () => dispatch(setManualSearchString(""));
}, []); }, []);


const handleSearch = (value) => { const handleSearch = (value) => {


const subcategories = useMemo(() => { const subcategories = useMemo(() => {
if (category) { if (category) {
if (manualSearchString)
return category.subcategories.filter((subcategory) =>
subcategory.name
.toLowerCase()
.includes(manualSearchString.toLowerCase())
);
return category.subcategories;
return adminSortMethod(
category.subcategories,
manualSearchString,
sorting
);
} }
return []; return [];
}, [category, manualSearchString]);
}, [category, manualSearchString, sorting.selectedSortOptionLocally]);
console.log("subc", categories); console.log("subc", categories);
const goBack = () => {
history.goBack();
};

return ( return (
<> <>
<AdminSubcategoriesPageContainer> <AdminSubcategoriesPageContainer>
handleSearch={handleSearch} handleSearch={handleSearch}
placeholder={t("admin.subcategories.placeholder")} placeholder={t("admin.subcategories.placeholder")}
/> />
<ButtonContainer onClick={goBack}>
<ArrowButton side={"left"}></ArrowButton>
<HeaderText>{t("admin.subcategories.goBack")}</HeaderText>
</ButtonContainer>
<AdminSubcategoriesHeader <AdminSubcategoriesHeader
hideSorting hideSorting
myOffers myOffers
/> />
</SubcategoriesList> </SubcategoriesList>
<AdminSubcategoriesHeader <AdminSubcategoriesHeader
hideSorting
sorting={sorting}
myOffers myOffers
subcategories subcategories
hideGrid hideGrid
hideBackButton hideBackButton
/> />
<SubcategoriesList> <SubcategoriesList>
{subcategories.map((category) => (
{subcategories.map((subcategory) => (
<CategoryCard <CategoryCard
categoryId={category._id}
subcategory subcategory
key={category._id}
category={category}
key={subcategory._id}
category={subcategory}
type="subcategories" type="subcategories"
hideSecondLabel hideSecondLabel
hideCheckButton hideCheckButton
{openedAddModal && ( {openedAddModal && (
<EditCategory <EditCategory
hideImagePicker hideImagePicker
categoryId={category._id}
setOpenedEditModal={setOpenedAddModal} setOpenedEditModal={setOpenedAddModal}
type="subcategories" type="subcategories"
method="add" method="add"

+ 33
- 4
src/pages/AdminHomePage/AdminSubcategoriesPage/AdminSubcategoriesPage.styled.js Прегледај датотеку

import { Box } from "@mui/material";
import { Box, Link, Typography } from "@mui/material";
import styled from "styled-components"; import styled from "styled-components";
import { PrimaryButton } from "../../../components/Buttons/PrimaryButton/PrimaryButton"; import { PrimaryButton } from "../../../components/Buttons/PrimaryButton/PrimaryButton";
import CategoryCard from "../../../components/Cards/CategoryCard/CategoryCard"; import CategoryCard from "../../../components/Cards/CategoryCard/CategoryCard";
export const AdminSubcategoriesHeader = styled(Header)` export const AdminSubcategoriesHeader = styled(Header)`
top: 4px; top: 4px;
@media (max-width: 600px) { @media (max-width: 600px) {
top: -10px;
height: 10px;
top: -65px;
&:nth-child(4) {
top: 4px;
& div:nth-child(2) {
margin-top: 0;
}
}
margin-top: 18px; margin-top: 18px;
& div {
& > div:nth-child(1) {
margin-top: 10px; margin-top: 10px;
} }
& div div:nth-child(1) {
& div div:nth-child(2) {
top: 22px; top: 22px;
} }
} }
`; `;
export const SubcategoriesList = styled(Box)` export const SubcategoriesList = styled(Box)`
padding-top: 18px; padding-top: 18px;
@media (max-width: 600px) {
&:nth-child(4) {
padding-top: 0;
}
}
` `
export const SponsoredCategoryCard = styled(CategoryCard)` export const SponsoredCategoryCard = styled(CategoryCard)`
background: ${selectedTheme.colors.backgroundSponsoredColor}; background: ${selectedTheme.colors.backgroundSponsoredColor};
right: 16px; right: 16px;
} }
` `
export const ButtonContainer = styled(Link)`
width:fit-content;
cursor:pointer;
display: flex;
justify-content: start;
align-items:center;
gap:12px;
text-decoration: none;
`
export const HeaderText = styled(Typography) `
font-family: ${selectedTheme.fonts.textFont};
line-height: 22px;
font-size: 16px;
color: ${selectedTheme.colors.primaryPurple};
text-decoration: none;
border-bottom: 1px dotted ${selectedTheme.colors.primaryPurple};
`

+ 4
- 3
src/pages/AdminHomePage/AdminUsersPage/AdminUsersPage.js Прегледај датотеку

import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { selectAllProfiles } from "../../../store/selectors/profileSelectors"; import { selectAllProfiles } from "../../../store/selectors/profileSelectors";
import { fetchAllProfiles } from "../../../store/actions/profile/profileActions";
import { fetchAllProfilesAsAdmin } from "../../../store/actions/profile/profileActions";
import { import {
AdminUsersHeader, AdminUsersHeader,
AdminUsersList, AdminUsersList,
[allUsers] [allUsers]
); );
useEffect(() => { useEffect(() => {
dispatch(fetchAllProfiles());
dispatch(fetchAllProfilesAsAdmin());
}, []); }, []);


const handleSearch = () => {}; const handleSearch = () => {};
<AdminUsersSearchField <AdminUsersSearchField
isAdmin isAdmin
handleSearch={handleSearch} handleSearch={handleSearch}
placeholder={t("admin.subcategories.placeholder")}
placeholder={t("admin.users.searchPlaceholder")}
/> />
<AdminUsersHeader <AdminUsersHeader
myOffers myOffers
hideSorting
category category
hideGrid hideGrid
isAdmin isAdmin

+ 21
- 0
src/request/apiEndpoints.js Прегледај датотеку

getProfile: "users/", getProfile: "users/",
editProfile: "users/{userId}", editProfile: "users/{userId}",
getAllProfiles: "users/companies", getAllProfiles: "users/companies",
editProfileAsAdmin: "admin/users/{userId}",
getAllProfilesAsAdmin: "admin/users",
deleteProfileAsAdmin: "admin/users/{userId}",
blockProfileAsAdmin: "admin/users/{userId}/block",
}, },
applications: { applications: {
application: "/applications/{applicationUid}", application: "/applications/{applicationUid}",
postReview: "/users/{userId}/reviews", postReview: "/users/{userId}/reviews",
removeReview: "/admin/reviews/{id}", removeReview: "/admin/reviews/{id}",
}, },
admin: {
categories: {
newCategory: "admin/categories",
editCategory: "admin/categories/{categoryId}",
deleteCategory: "admin/categories/{categoryId}",
},
subcategories: {
newSubcategory: "admin/categories/{categoryId}",
editSubcategory: "admin/categories/{categoryId}/{subcategoryName}",
deleteSubcategory: "admin/categories/{categoryId}/{subcategoryName}",
},
locations: {
newLocation: "admin/locations",
editLocation: "admin/locations/{locationId}",
deleteLocation: "admin/locations/{locationId}",
},
},
}; };

+ 48
- 1
src/request/categoriesRequest.js Прегледај датотеку

import { getRequest } from ".";
import {
deleteRequest,
getRequest,
postRequest,
putRequest,
replaceInUrl,
} from ".";
import apiEndpoints from "./apiEndpoints"; import apiEndpoints from "./apiEndpoints";


export const attemptFetchCategories = () => export const attemptFetchCategories = () =>
getRequest(apiEndpoints.offers.categories); getRequest(apiEndpoints.offers.categories);

export const attemptAddNewCategory = (payload) =>
postRequest(apiEndpoints.admin.categories.newCategory, payload.body);

export const attemptEditCategory = (payload) =>
putRequest(
replaceInUrl(apiEndpoints.admin.categories.editCategory, {
categoryId: payload.categoryId,
}),
payload.body
);
export const attemptDeleteCategory = (payload) =>
deleteRequest(
replaceInUrl(apiEndpoints.admin.categories.deleteCategory, {
categoryId: payload.categoryId,
})
);

export const attemptAddNewSubcategory = (payload) =>
postRequest(
replaceInUrl(apiEndpoints.admin.subcategories.newSubcategory, {
categoryId: payload.categoryId,
}),
payload.body
);
export const attemptEditSubcategory = (payload) =>
putRequest(
replaceInUrl(apiEndpoints.admin.subcategories.editSubcategory, {
categoryId: payload.categoryId,
subcategoryName: payload.subcategoryName,
}),
payload.body
);
export const attemptDeleteSubcategory = (payload) =>
deleteRequest(
replaceInUrl(apiEndpoints.admin.subcategories.deleteSubcategory, {
categoryId: payload.categoryId,
subcategoryName: payload.subcategoryName,
})
);

+ 25
- 2
src/request/locationsRequest.js Прегледај датотеку

import { getRequest } from ".";
import {
deleteRequest,
getRequest,
postRequest,
putRequest,
replaceInUrl,
} from ".";
import apiEndpoints from "./apiEndpoints"; import apiEndpoints from "./apiEndpoints";


export const attemptFetchLocations = () => export const attemptFetchLocations = () =>
getRequest(apiEndpoints.offers.locations);
getRequest(apiEndpoints.offers.locations);
export const attemptAddNewLocation = (payload) =>
postRequest(apiEndpoints.admin.locations.newLocation, payload.body);

export const attemptEditLocation = (payload) =>
putRequest(
replaceInUrl(apiEndpoints.admin.locations.editLocation, {
locationId: payload.locationId,
}),
payload.body
);

export const attemptDeleteLocation = (payload) =>
deleteRequest(
replaceInUrl(apiEndpoints.admin.locations.deleteLocation, {
locationId: payload.locationId,
})
);

+ 26
- 2
src/request/profileRequest.js Прегледај датотеку

import { getRequest, putRequest, replaceInUrl } from ".";
import {
deleteRequest,
getRequest,
patchRequest,
putRequest,
replaceInUrl,
} from ".";
import apiEndpoints from "./apiEndpoints"; import apiEndpoints from "./apiEndpoints";


export const attemptFetchProfile = (payload) => export const attemptFetchProfile = (payload) =>


export const attemptFetchAllProfiles = () => export const attemptFetchAllProfiles = () =>
getRequest(apiEndpoints.users.getAllProfiles); getRequest(apiEndpoints.users.getAllProfiles);
export const attemptFetchAllProfilesAsAdmin = () =>
getRequest(apiEndpoints.users.getAllProfilesAsAdmin);


export const attemptEditProfile = (payload, requestData) => { export const attemptEditProfile = (payload, requestData) => {
// putRequest(apiEndpoints.users.editProfile + "/" + payload, requestData);
return putRequest( return putRequest(
replaceInUrl(apiEndpoints.users.editProfile, { userId: payload }), replaceInUrl(apiEndpoints.users.editProfile, { userId: payload }),
requestData requestData
); );
}; };
export const attemptEditProfileAsAdmin = (payload, requestData) =>
putRequest(
replaceInUrl(apiEndpoints.users.editProfileAsAdmin, { userId: payload }),
requestData
);
export const attemptDeleteProfileAsAdmin = (payload) =>
deleteRequest(
replaceInUrl(apiEndpoints.users.deleteProfileAsAdmin, {
userId: payload.userId,
})
);
export const attemptBlockProfileAsAdmin = (payload) =>
patchRequest(
replaceInUrl(apiEndpoints.users.blockProfileAsAdmin, {
userId: payload.userId,
})
);

+ 10
- 0
src/store/actions/admin/adminActionConstants.js Прегледај датотеку

import {
createErrorType,
createFetchType,
createSuccessType,
} from "../actionHelpers";

const ADMIN_SCOPE = "ADMIN";
export const ADMIN_FETCH = createFetchType(ADMIN_SCOPE);
export const ADMIN_FETCH_SUCCESS = createSuccessType(ADMIN_SCOPE);
export const ADMIN_FETCH_ERROR = createErrorType(ADMIN_SCOPE);

+ 16
- 0
src/store/actions/admin/adminActions.js Прегледај датотеку

import {
ADMIN_FETCH,
ADMIN_FETCH_ERROR,
ADMIN_FETCH_SUCCESS,
} from "./adminActionConstants";

export const fetchAdminMethod = (payload) => ({
type: ADMIN_FETCH,
payload,
});
export const fetchAdminMethodSuccess = () => ({
type: ADMIN_FETCH_SUCCESS,
});
export const fetchAdminMethodError = () => ({
type: ADMIN_FETCH_ERROR,
});

+ 10
- 0
src/store/actions/profile/profileActionConstants.js Прегледај датотеку

export const PROFILE_ALL_SUCCESS = createSuccessType(PROFILE_ALL_SCOPE); export const PROFILE_ALL_SUCCESS = createSuccessType(PROFILE_ALL_SCOPE);
export const PROFILE_ALL_ERROR = createErrorType(PROFILE_ALL_SCOPE); export const PROFILE_ALL_ERROR = createErrorType(PROFILE_ALL_SCOPE);


export const PROFILE_ALL_ADMIN_SCOPE = "PROFILE_ALL_ADMIN_SCOPE";
export const PROFILE_ALL_ADMIN_FETCH = createFetchType(PROFILE_ALL_ADMIN_SCOPE);
export const PROFILE_ALL_ADMIN_SUCCESS = createSuccessType(PROFILE_ALL_ADMIN_SCOPE);
export const PROFILE_ALL_ADMIN_ERROR = createErrorType(PROFILE_ALL_ADMIN_SCOPE);

export const PROFILE_MINE_SCOPE = "PROFILE_MINE_SCOPE"; export const PROFILE_MINE_SCOPE = "PROFILE_MINE_SCOPE";
export const PROFILE_MINE_FETCH = createFetchType(PROFILE_MINE_SCOPE); export const PROFILE_MINE_FETCH = createFetchType(PROFILE_MINE_SCOPE);
export const PROFILE_MINE_FETCH_SUCCESS = createSuccessType(PROFILE_MINE_SCOPE); export const PROFILE_MINE_FETCH_SUCCESS = createSuccessType(PROFILE_MINE_SCOPE);
export const PROFILE_EDIT_SUCCESS = createSuccessType(PROFILE_EDIT_SCOPE); export const PROFILE_EDIT_SUCCESS = createSuccessType(PROFILE_EDIT_SCOPE);
export const PROFILE_EDIT_ERROR = createErrorType(PROFILE_EDIT_SCOPE); export const PROFILE_EDIT_ERROR = createErrorType(PROFILE_EDIT_SCOPE);


const PROFILE_EDIT_ADMIN_SCOPE = "PROFILE_EDIT_ADMIN_SCOPE";
export const PROFILE_EDIT_ADMIN = createFetchType(PROFILE_EDIT_ADMIN_SCOPE);
export const PROFILE_EDIT_ADMIN_SUCCESS = createSuccessType(PROFILE_EDIT_ADMIN_SCOPE);
export const PROFILE_EDIT_ADMIN_ERROR = createErrorType(PROFILE_EDIT_ADMIN_SCOPE);

export const PROFILE_CLEAR = createClearType("PROFILE_CLEAR"); export const PROFILE_CLEAR = createClearType("PROFILE_CLEAR");

+ 32
- 2
src/store/actions/profile/profileActions.js Прегледај датотеку

PROFILE_SET, PROFILE_SET,
PROFILE_SUCCESS, PROFILE_SUCCESS,
PROFILE_EDIT, PROFILE_EDIT,
PROFILE_MINE_FETCH_SUCCESS,
PROFILE_EDIT_SUCCESS, PROFILE_EDIT_SUCCESS,
PROFILE_MINE_FETCH_ERROR,
PROFILE_EDIT_ERROR, PROFILE_EDIT_ERROR,
PROFILE_EDIT_ADMIN,
PROFILE_EDIT_ADMIN_SUCCESS,
PROFILE_EDIT_ADMIN_ERROR,
PROFILE_MINE_FETCH_SUCCESS,
PROFILE_MINE_FETCH_ERROR,
PROFILE_ALL_FETCH, PROFILE_ALL_FETCH,
PROFILE_ALL_SUCCESS, PROFILE_ALL_SUCCESS,
PROFILE_ALL_ERROR, PROFILE_ALL_ERROR,
PROFILE_ALL_ADMIN_FETCH,
PROFILE_ALL_ADMIN_SUCCESS,
PROFILE_ALL_ADMIN_ERROR,
PROFILE_ALL_SET, PROFILE_ALL_SET,
} from "./profileActionConstants"; } from "./profileActionConstants";


payload, payload,
}); });


export const fetchAllProfilesAsAdmin = (payload) => ({
type: PROFILE_ALL_ADMIN_FETCH,
payload,
});
export const fetchAllProfilesAsAdminSuccess = (payload) => ({
type: PROFILE_ALL_ADMIN_SUCCESS,
payload,
});
export const fetchAllProfilesAsAdminError = (payload) => ({
type: PROFILE_ALL_ADMIN_ERROR,
payload,
});

export const fetchMineProfile = () => ({ export const fetchMineProfile = () => ({
type: PROFILE_MINE_FETCH, type: PROFILE_MINE_FETCH,
}); });
type: PROFILE_EDIT_ERROR, type: PROFILE_EDIT_ERROR,
}); });


export const editProfileAsAdmin = (payload) => ({
type: PROFILE_EDIT_ADMIN,
payload,
});
export const editProfileAsAdminSuccess = () => ({
type: PROFILE_EDIT_ADMIN_SUCCESS,
});
export const editProfileAsAdminError = () => ({
type: PROFILE_EDIT_ADMIN_ERROR,
});

export const clearProfile = () => ({ export const clearProfile = () => ({
type: PROFILE_CLEAR, type: PROFILE_CLEAR,
}); });

+ 218
- 0
src/store/saga/adminSaga.js Прегледај датотеку

import { all, call, put, takeLatest } from "@redux-saga/core/effects";
import {
ADD_TYPE,
DELETE_TYPE,
EDIT_TYPE,
} from "../../constants/adminMethodConstants";
import {
CATEGORIES_TYPE,
LOCATIONS_TYPE,
SUBCATEGORIES_TYPE,
USERS_BLOCK_TYPE,
USERS_DELETE_TYPE,
} from "../../constants/adminTypeConstants";
import {
attemptAddNewCategory,
attemptAddNewSubcategory,
attemptDeleteCategory,
attemptDeleteSubcategory,
attemptEditCategory,
attemptEditSubcategory,
} from "../../request/categoriesRequest";
import {
attemptAddNewLocation,
attemptDeleteLocation,
attemptEditLocation,
} from "../../request/locationsRequest";
import { attemptBlockProfileAsAdmin, attemptDeleteProfileAsAdmin } from "../../request/profileRequest";
// import { attemptAddNewCategory } from "../../request/categoriesRequest";
import { ADMIN_FETCH } from "../actions/admin/adminActionConstants";
import {
fetchAdminMethodError,
fetchAdminMethodSuccess,
} from "../actions/admin/adminActions";
import { fetchCategories } from "../actions/categories/categoriesActions";
import { fetchLocations } from "../actions/locations/locationsActions";
import { fetchAllProfilesAsAdmin } from "../actions/profile/profileActions";

function* editCategory(payload) {
try {
yield call(attemptEditCategory, {
categoryId: payload.id,
body: { name: payload.values.title },
});
yield put(fetchCategories());
} catch (e) {
yield call(console.log, e);
}
}
function* addCategory(payload) {
try {
let dataToSend = new FormData();
dataToSend.append("name", payload.values.title);
dataToSend.append("file", payload.values.image);
dataToSend.append("subcategories", JSON.stringify([]));
yield call(attemptAddNewCategory, {
body: dataToSend,
});
yield put(fetchCategories());
} catch (e) {
yield call(console.log, e);
}
}

function* deleteCategory(payload) {
try {
yield call(attemptDeleteCategory, {
categoryId: payload.id,
});
yield put(fetchCategories());
} catch (e) {
yield call(console.log, e);
}
}

function* editSubcategory(payload) {
try {
yield call(attemptEditSubcategory, {
categoryId: payload.id,
subcategoryName: payload.name,
body: { name: payload.values.title },
});
yield put(fetchCategories());
} catch (e) {
yield call(console.log, e);
}
}
function* addSubcategory(payload) {
try {
yield call(attemptAddNewSubcategory, {
categoryId: payload.id,
body: { name: payload.values.title },
});
yield put(fetchCategories());
} catch (e) {
yield call(console.log, e);
}
}

function* deleteSubcategory(payload) {
try {
yield call(attemptDeleteSubcategory, {
categoryId: payload.id,
subcategoryName: payload.name,
});
yield put(fetchCategories());
} catch (e) {
yield call(console.log, e);
}
}

function* editLocation(payload) {
try {
yield call(attemptEditLocation, {
locationId: payload.id,
body: { city: payload.values.title },
});
yield put(fetchLocations());
} catch (e) {
yield call(console.log, e);
}
}
function* addLocation(payload) {
try {
yield call(attemptAddNewLocation, {
locationId: payload.id,
body: { city: payload.values.title },
});
yield put(fetchLocations());
} catch (e) {
yield call(console.log, e);
}
}

function* deleteLocation(payload) {
try {
yield call(attemptDeleteLocation, {
locationId: payload.id,
});
yield put(fetchLocations());
} catch (e) {
yield call(console.log, e);
}
}
function* deleteUser(payload) {
try {
yield call(attemptDeleteProfileAsAdmin, { userId: payload.id });
yield put(fetchAllProfilesAsAdmin());
} catch (e) {
yield call(console.log, e);
}
}
function* blockUser(payload) {
try {
yield call(attemptBlockProfileAsAdmin, { userId: payload.id });
yield put(fetchAllProfilesAsAdmin());
} catch (e) {
yield call(console.log, e);
}
}

function* fetchAdminMethod({ payload }) {
try {
yield call(console.log, "admin", payload);
if (payload.type === CATEGORIES_TYPE) {
if (payload.method === ADD_TYPE)
yield call(addCategory, { values: payload.values, id: payload.id });
else if (payload.method === EDIT_TYPE)
yield call(editCategory, { values: payload.values, id: payload.id });
else if (payload.method === DELETE_TYPE)
yield call(deleteCategory, { id: payload.id });
} else if (payload.type === SUBCATEGORIES_TYPE) {
if (payload.method === EDIT_TYPE)
yield call(editSubcategory, {
values: payload.values,
id: payload.categoryId,
name: payload.name,
});
else if (payload.method === DELETE_TYPE)
yield call(deleteSubcategory, {
id: payload.categoryId,
name: payload.name,
});
else if (payload.method === ADD_TYPE)
yield call(addSubcategory, {
values: payload.values,
id: payload.categoryId,
});
} else if (payload.type === LOCATIONS_TYPE) {
if (payload.method === ADD_TYPE)
yield call(addLocation, {
values: payload.values,
id: payload.id,
});
else if (payload.method === EDIT_TYPE)
yield call(editLocation, {
values: payload.values,
id: payload.id,
});
else if (payload.method === DELETE_TYPE)
yield call(deleteLocation, {
id: payload.id,
});
} else if (payload.type === USERS_DELETE_TYPE) {
yield call(deleteUser, { id: payload.id });
} else if (payload.type === USERS_BLOCK_TYPE) {
yield call(blockUser, { id: payload.id });
}
if (payload.handleApiResponseSuccess)
yield call(payload.handleApiResponseSuccess);
yield put(fetchAdminMethodSuccess());
} catch (e) {
yield put(fetchAdminMethodError());
}
}

export default function* adminSaga() {
yield all([takeLatest(ADMIN_FETCH, fetchAdminMethod)]);
}

+ 3
- 1
src/store/saga/index.js Прегледај датотеку

import { all } from 'redux-saga/effects'; import { all } from 'redux-saga/effects';
import adminSaga from './adminSaga';
import categoriesSaga from './categoriesSaga'; import categoriesSaga from './categoriesSaga';
import chatSaga from './chatSaga'; import chatSaga from './chatSaga';
import counterSaga from './counterSaga'; import counterSaga from './counterSaga';
queryStringSaga(), queryStringSaga(),
exchangeSaga(), exchangeSaga(),
reviewSaga(), reviewSaga(),
counterSaga()
counterSaga(),
adminSaga()
]); ]);
} }

+ 54
- 29
src/store/saga/profileSaga.js Прегледај датотеку

import { all, call, put, takeLatest, select } from "@redux-saga/core/effects"; import { all, call, put, takeLatest, select } from "@redux-saga/core/effects";
import { import {
attemptEditProfile, attemptEditProfile,
attemptEditProfileAsAdmin,
attemptFetchAllProfiles, attemptFetchAllProfiles,
attemptFetchAllProfilesAsAdmin,
attemptFetchProfile, attemptFetchProfile,
} from "../../request/profileRequest"; } from "../../request/profileRequest";
import { import {
PROFILE_MINE_FETCH, PROFILE_MINE_FETCH,
PROFILE_EDIT, PROFILE_EDIT,
PROFILE_ALL_FETCH, PROFILE_ALL_FETCH,
PROFILE_EDIT_ADMIN,
PROFILE_ALL_ADMIN_FETCH,
} from "../actions/profile/profileActionConstants"; } from "../actions/profile/profileActionConstants";
import { import {
editProfileAsAdminError,
editProfileAsAdminSuccess,
editProfileError, editProfileError,
editProfileSuccess, editProfileSuccess,
fetchAllProfilesAsAdminError,
fetchAllProfilesAsAdminSuccess,
fetchAllProfilesError, fetchAllProfilesError,
fetchAllProfilesSuccess, fetchAllProfilesSuccess,
fetchErrorProfile, fetchErrorProfile,
} }
} }


function* changeMineProfile(payload) {
function* fetchAllProfilesAsAdmin() {
try { try {
// console.log(payload);
// let image;
// if (payload.payload.firmLogo) {
// image = payload.payload.firmLogo;
// } else if (payload.payload.firmLogo === "") {
// image = "";
// }

// const reqData = {
// company: {
// name: payload.payload.firmName,
// PIB: payload.payload.firmPIB,
// contacts: {
// telephone: payload.payload.firmPhone.toString(),
// location: payload.payload.firmLocation ?? "",
// web: payload.payload.firmWebsite,
// },
// },
// image: image,
// };

// if (payload.payload.firmLogo?.includes("https")) delete reqData.image;
// if (reqData.company.contacts.telephone.length === 0)
// delete reqData.company.contacts.telephone;
// if (reqData.company.contacts.location.length === 0)
// delete reqData.company.contacts.location;
// if (reqData.company.contacts.web.length === 0)
// delete reqData.company.contacts.web;
const data = yield call(attemptFetchAllProfilesAsAdmin);
if (data) yield put(setAllProfiles(data.data.users));
yield put(fetchAllProfilesAsAdminSuccess());
} catch (e) {
yield put(fetchAllProfilesAsAdminError());
console.dir(e);
}
}


function* changeMineProfile(payload) {
try {
const requestBody = new FormData(); const requestBody = new FormData();
if (typeof payload.payload.firmLogo !== "string") if (typeof payload.payload.firmLogo !== "string")
requestBody.append("file", payload.payload.firmLogo); requestBody.append("file", payload.payload.firmLogo);
} }
} }


function* changeProfileAsAdmin(payload) {
try {
const requestBody = new FormData();
console.log(payload);
if (typeof payload.payload?.firmLogo !== "string")
requestBody.append("file", payload.payload.firmLogo);
requestBody.append("company[name]", payload.payload.firmName);
requestBody.append("company[PIB]", payload.payload.firmPIB);
if (payload.payload.firmPhone.toString().length !== 0)
requestBody.append(
"company[contacts][telephone]",
payload.payload.firmPhone
);
if (payload.payload.firmLocation.toString().length !== 0)
requestBody.append(
"company[contacts][location]",
payload.payload.firmLocation
);
if (payload.payload.firmWebsite.toString().length !== 0)
requestBody.append("company[contacts][web]", payload.payload.firmWebsite);

const userId = payload.payload.userId;
yield call(attemptEditProfileAsAdmin, userId, requestBody);
yield put(editProfileAsAdminSuccess());
if (payload.payload.handleApiResponseSuccess) {
yield call(payload.payload.handleApiResponseSuccess);
}
} catch (e) {
yield put(editProfileAsAdminError());
console.dir(e);
}
}

export default function* profileSaga() { export default function* profileSaga() {
yield all([ yield all([
takeLatest(PROFILE_FETCH, fetchProfile), takeLatest(PROFILE_FETCH, fetchProfile),
takeLatest(PROFILE_MINE_FETCH, fetchMineProfile), takeLatest(PROFILE_MINE_FETCH, fetchMineProfile),
takeLatest(PROFILE_EDIT, changeMineProfile), takeLatest(PROFILE_EDIT, changeMineProfile),
takeLatest(PROFILE_ALL_FETCH, fetchAllProfiles), takeLatest(PROFILE_ALL_FETCH, fetchAllProfiles),
takeLatest(PROFILE_ALL_ADMIN_FETCH, fetchAllProfilesAsAdmin),
takeLatest(PROFILE_EDIT_ADMIN, changeProfileAsAdmin),
]); ]);
} }

+ 42
- 0
src/util/helpers/adminSortHelper.js Прегледај датотеку

import { sortCategoriesEnum } from "../../enums/sortEnum";

export const adminSortMethod = (arrayOfItems, manualSearchString, sorting) => {
console.log(arrayOfItems);
console.log(sorting);
let arrayOfItemsToReturn = [...arrayOfItems];
if (manualSearchString)
arrayOfItemsToReturn = arrayOfItems.filter(
(item) =>
item?.city?.toLowerCase()?.includes(manualSearchString.toLowerCase()) ||
item?.name?.toLowerCase()?.includes(manualSearchString.toLowerCase())
);
if (sorting?.selectedSortOptionLocally !== sortCategoriesEnum.INITIAL) {
if (sorting?.selectedSortOptionLocally === sortCategoriesEnum.POPULAR) {
arrayOfItemsToReturn.sort((a, b) => b.offerCount - a.offerCount);
}
if (sorting?.selectedSortOptionLocally === sortCategoriesEnum.OLD) {
arrayOfItemsToReturn.sort((a, b) => {
const firstCreated = new Date(
a?._created || new Date(1970, 1).toISOString()
);
const secondCreated = new Date(
b?._created || new Date(1970, 1).toISOString()
);
console.log(firstCreated);
return firstCreated - secondCreated;
});
}
if (sorting?.selectedSortOptionLocally === sortCategoriesEnum.NEW) {
arrayOfItemsToReturn.sort((a, b) => {
const firstCreated = new Date(
a?._created || new Date(1970, 1).toISOString()
);
const secondCreated = new Date(
b?._created || new Date(1970, 1).toISOString()
);
return secondCreated - firstCreated;
});
}
}
return arrayOfItemsToReturn;
};

+ 3
- 1
src/util/helpers/imageUrlGetter.js Прегледај датотеку

profileCard: "profileCard", profileCard: "profileCard",
createReviewCard: "createReviewCard", createReviewCard: "createReviewCard",
carousel: "carousel", carousel: "carousel",
adminProfileCard: "reviewCard"
adminProfileCard: "reviewCard",
categoryIcon: "categoryIcon"
}; };


const cloudFlareVariants = { const cloudFlareVariants = {
createReviewCard: "primaryMobile", createReviewCard: "primaryMobile",
carousel: "carousel", carousel: "carousel",
carouselMobile: "carouselMobile", carouselMobile: "carouselMobile",
categoryIcon: "categoryIcon"
}; };
export const getImageUrl = (imageUrl, variant, isMobile = false) => { export const getImageUrl = (imageUrl, variant, isMobile = false) => {
let imageVariant = ""; let imageVariant = "";

+ 2
- 1
src/util/helpers/routeHelpers.js Прегледај датотеку

dynamicRouteMatches(ADMIN_SUBCATEGORIES_PAGE) || dynamicRouteMatches(ADMIN_SUBCATEGORIES_PAGE) ||
routeMatches(ADMIN_LOCATIONS_PAGE) || routeMatches(ADMIN_LOCATIONS_PAGE) ||
routeMatches(ADMIN_PAYMENT_PAGE) || routeMatches(ADMIN_PAYMENT_PAGE) ||
dynamicRouteMatches(ADMIN_SINGLE_USER_PAGE)
dynamicRouteMatches(ADMIN_SINGLE_USER_PAGE) ||
isInRoute(ADMIN_HOME_PAGE)
) )
return true; return true;
return false; return false;

Loading…
Откажи
Сачувај