Sfoglia il codice sorgente

Partly finished feature 688

feature/688
jovan.cirkovic 3 anni fa
parent
commit
7c00f1e951
44 ha cambiato i file con 807 aggiunte e 114 eliminazioni
  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 Vedi File

@@ -24,7 +24,7 @@ const CategoryCard = (props) => {
const [openedDeleteModal, setOpenedDeleteModal] = useState(false);
const [openedEditModal, setOpenedEditModal] = useState(false);
const navigateToCategory = () => {
if (!props.subcategory) {
if (!props.hideCheckButton) {
history.push(
replaceInRoute(ADMIN_SUBCATEGORIES_PAGE, {
categoryId: props.category._id,
@@ -32,12 +32,12 @@ const CategoryCard = (props) => {
);
}
};
console.log(props);
return (
<>
<CategoryCardContainer className={props.className}>
<CategoryCardLeftContainer>
<CategoryCardName
image={props?.category?.image}
categoryName={props?.category?.name || props?.category?.city}
onClick={navigateToCategory}
/>
@@ -67,6 +67,7 @@ const CategoryCard = (props) => {
</CategoryCardContainer>
{openedDeleteModal && (
<DeleteCategory
categoryId={props.categoryId}
setOpenedDeleteModal={setOpenedDeleteModal}
subcategory={props.subcategory}
category={props.category}
@@ -78,6 +79,7 @@ const CategoryCard = (props) => {
hideImagePicker={props.type !== "categories"}
setOpenedEditModal={setOpenedEditModal}
category={props.category}
categoryId={props.categoryId}
subcategory={props.subcategory}
type={props.type}
method="edit"
@@ -98,6 +100,7 @@ CategoryCard.propTypes = {
className: PropTypes.string,
subcategory: PropTypes.bool,
type: PropTypes.string,
categoryId: PropTypes.string,
};

export default CategoryCard;

+ 15
- 1
src/components/Cards/CategoryCard/CategoryCardName/CategoryCardName.js Vedi File

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

const CategoryCardName = (props) => {
return (
<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>
);
};
@@ -17,6 +30,7 @@ CategoryCardName.propTypes = {
children: PropTypes.node,
categoryName: PropTypes.string,
onClick: PropTypes.func,
image: PropTypes.string,
};

export default CategoryCardName;

+ 37
- 3
src/components/Cards/CategoryCard/CategoryCardName/CategoryCardName.styled.js Vedi File

@@ -3,19 +3,53 @@ import styled from "styled-components";
import selectedTheme from "../../../../themes";

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)`
font-family: ${selectedTheme.fonts.textFont};
font-weight: 700;
font-size: 16px;
line-height: 21px;
padding-top: 12px;
max-height: 42px;
overflow: hidden;
text-overflow: ellipsis;
color: ${selectedTheme.colors.primaryPurple};
cursor: pointer;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
@media (max-width: 600px) {
font-size: 18px;
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 Vedi File

@@ -11,6 +11,9 @@ export const CheckButton = styled(PrimaryButton)`
background-color: ${selectedTheme.colors.primaryPurple} !important;
color: white !important;
}
@media (max-width: 1390px) {
display: none;
}
@media (max-width: 850px) {
display: none;
}

+ 5
- 0
src/components/Cards/ItemDetailsCard/Information/Information.styled.js Vedi File

@@ -17,6 +17,11 @@ export const InfoText = styled(Typography)`
text-transform: capitalize;
color: ${selectedTheme.colors.primaryText};
font-size: 12px;
max-width: 250px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-height: 16px;
@media (max-width: 600px) {
font-size: 12px;
}

+ 1
- 0
src/components/Cards/ItemDetailsCard/ItemDetailsCard.styled.js Vedi File

@@ -27,6 +27,7 @@ export const ItemDetailsCardContainer = styled(Container)`
padding: 18px;
max-width: 2000px;
position: relative;
width: auto;
${(props) => !props.previewCard && `height: 654px;`}
/* height: 654px; */
/* padding-bottom: 70px; */

+ 5
- 0
src/components/Cards/LabeledCard/LabeledCard.styled.js Vedi File

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

+ 5
- 0
src/components/Cards/OfferCard/OfferCard.styled.js Vedi File

@@ -236,6 +236,11 @@ export const DetailText = styled(Typography)`
line-height: 16px;
font-size: 12px;
position: relative;
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-height: 16px;
top: -2px;
left: 3px;
`;

+ 1
- 0
src/components/Cards/ProfileCard/BigProfileCard/BigProfileCard.js Vedi File

@@ -100,6 +100,7 @@ const BigProfileCard = (props) => {
)}
{deleteOrEditModal.show && (
<DeleteCategory
customId={props.profile._id}
setOpenedDeleteModal={closeModalHandler}
type={deleteOrEditModal.type}
customLabeledCard={<UserLabeledCard user={props.profile} />}

+ 6
- 5
src/components/Cards/ProfileCard/EditProfile/EditProfile.js Vedi File

@@ -20,7 +20,8 @@ import { ReactComponent as CloseIcon } from "../../../../assets/images/svg/close
import { useTranslation } from "react-i18next";
import {
editProfile,
fetchAllProfiles,
editProfileAsAdmin,
fetchAllProfilesAsAdmin,
fetchMineProfile,
} from "../../../../store/actions/profile/profileActions";
import { useDispatch, useSelector } from "react-redux";
@@ -63,17 +64,17 @@ const EditProfile = (props) => {
const handleApiResponseSuccess = () => {
if (dynamicRouteMatches(PROFILE_PAGE)) dispatch(fetchMineProfile(userId));
if (routeMatches(ADMIN_USERS_PAGE) || routeMatches(ADMIN_HOME_PAGE))
dispatch(fetchAllProfiles());
dispatch(fetchAllProfilesAsAdmin());
props.reFetchProfile();
};

const handleSubmit = (values) => {
if (props.isAdmin) {
dispatch(
editProfile({
editProfileAsAdmin({
userId: props.userId,
...values,
firmLogo: profileImage,
firmLogo: profileImage === props.profile.image ? null : profileImage,
handleApiResponseSuccess,
})
);
@@ -81,7 +82,7 @@ const EditProfile = (props) => {
dispatch(
editProfile({
...values,
firmLogo: profileImage,
firmLogo: profileImage === props.profile.image ? null : profileImage,
handleApiResponseSuccess,
})
);

+ 1
- 0
src/components/Cards/ProfileCard/ProfileCard.js Vedi File

@@ -178,6 +178,7 @@ const ProfileCard = (props) => {
{editProfileModal && (
<EditProfile
profile={profile}
isAdmin={props.isAdmin}
closeModalHandler={closeModalHandler}
reFetchProfile={reFetchProfile}
/>

+ 1
- 13
src/components/Cards/ProfileCard/SkeletonProfileCard/SkeletonProfileCard.styled.js Vedi File

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

const skeletonAnimation = keyframes`
0% {
opacity: 1;
}
50% {
opacity: 0.63;
}
100% {
opacity: 1
}
`;
const skeletonBackgroundAnimation = keyframes`
0% {
opacity: 1;
@@ -27,7 +15,7 @@ const skeletonBackgroundAnimation = keyframes`

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

+ 10
- 0
src/components/Dropdown/DropdownList/DropdownList.styled.js Vedi File

@@ -19,6 +19,14 @@ export const DropdownTitle = styled(Typography)`
padding-bottom: 10px;
padding-top: 5px;
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};
color: ${(props) =>
props.disabled
@@ -28,6 +36,8 @@ export const DropdownTitle = styled(Typography)`
: selectedTheme.coloros.primaryText};
@media (max-width: 600px) {
font-size: 14px;
width: 260px;
max-width: 260px;
}
`;


+ 1
- 1
src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/Category/CategoryDetail.js Vedi File

@@ -16,7 +16,7 @@ const CategoryDetail = (props) => {
<LocationIcon />
</DetailIcon>
<DetailText ismyprofile={props.isMyProfile}>
{offer.offer.location.city}
{offer.offer?.location?.city}
</DetailText>
</DetailContainer>
);

+ 7
- 6
src/components/MarketPlace/Header/Header.js Vedi File

@@ -56,8 +56,8 @@ const Header = (props) => {
const sorting = props?.sorting;
const headerString = useSelector(selectHeaderString);
const { isMobile } = useIsMobile();
// Changing header string on refresh or on load

// Changing header string on refresh or on load
const altString = useMemo(() => {
if (!props?.users) {
if (sorting?.selectedSortOption === sortEnum.OLD) {
@@ -104,6 +104,7 @@ const Header = (props) => {
});

const handleChangeSelect = (event) => {
console.log(sorting);
if (!props.users) sorting?.changeSorting(event.target.value);
};
const handleClickCategory = () => {
@@ -227,14 +228,14 @@ const Header = (props) => {
<SelectOption style={{ display: "none" }} value="default">
{t("reviews.sortBy")}
</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 (
<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>
);
})}

+ 24
- 0
src/components/Modals/DeleteCategory/DeleteCategory.js Vedi File

@@ -14,8 +14,12 @@ import LabeledCard from "../../Cards/LabeledCard/LabeledCard";
import { useTranslation } from "react-i18next";
import { useMemo } from "react";
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 dispatch = useDispatch();
const { t } = useTranslation();
console.log(props.category);
const handleCancel = () => {
@@ -24,6 +28,23 @@ const DeleteCategory = (props) => {
const reassuranceText = useMemo(() => {
return t(`admin.${props.type}.reassuranceDelete`);
}, [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 (
<>
<BackdropComponent
@@ -55,6 +76,7 @@ const DeleteCategory = (props) => {
<SaveButton
variant="outlined"
buttoncolor={selectedTheme.colors.primaryPurple}
onClick={handleSubmit}
>
{t(`admin.${props.type}.delete`)}
</SaveButton>
@@ -73,6 +95,8 @@ DeleteCategory.propTypes = {
customLabeledCardWidth: PropTypes.string,
customLabeledCardHeight: PropTypes.string,
customLabeledCardIcon: PropTypes.node,
categoryId: PropTypes.string,
customId: PropTypes.string,
};

export default DeleteCategory;

+ 5
- 0
src/components/Modals/DeleteCategory/DeleteCategory.styled.js Vedi File

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

+ 33
- 7
src/components/Modals/EditCategory/EditCategory.js Vedi File

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

const EditCategory = (props) => {
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(() => {
return t(`admin.${props.type}.${props.method}.title`);
}, [props.type, props.method]);
@@ -44,17 +49,36 @@ const EditCategory = (props) => {
setImage(image);
formik.setFieldValue("image", image);
};
const handleApiResponseSuccess = () => {
if (clickedOnNext) {
formik.resetForm();
inputRef.current.focus();
} else props.setOpenedEditModal(false);
};
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({
initialValues: {
image: "",
title: props?.category?.name || props?.category?.city || "",
title: props?.category?.name || props?.category?.city || "",
},
onSubmit: handleSubmit,
});
const handleClick = (next) => {
if (next !== clickedOnNext) setClickedOnNext(next);
formik.handleSubmit();
};
console.log(props);
return (
<>
@@ -78,6 +102,7 @@ const EditCategory = (props) => {
<FieldLabel leftText={fieldLabel} />
<CategoryTitleField
name="title"
ref={inputRef}
placeholder={placeholder}
italicPlaceholder
margin="normal"
@@ -94,7 +119,7 @@ const EditCategory = (props) => {
showSecondButton={props.showSecondButton}
variant={props.firstOutlined ? "outlined" : "contained"}
height="48px"
onClick={formik.handleSubmit}
onClick={() => handleClick(true)}
>
{firstButtonText}
</SaveButton>
@@ -102,7 +127,7 @@ const EditCategory = (props) => {
<SaveButton
variant={props.secondOutlined ? "outlined" : "contained"}
showSecondButton={props.showSecondButton}
onClick={formik.handleSubmit}
onClick={() => handleClick(false)}
>
{secondButtonText}
</SaveButton>
@@ -122,6 +147,7 @@ EditCategory.propTypes = {
method: PropTypes.string,
firstOutlined: PropTypes.bool,
secondOutlined: PropTypes.bool,
categoryId: PropTypes.string,
};

EditCategory.defaultProps = {

+ 3
- 0
src/constants/adminMethodConstants.js Vedi File

@@ -0,0 +1,3 @@
export const EDIT_TYPE = "edit";
export const DELETE_TYPE = "delete";
export const ADD_TYPE = "add";

+ 7
- 0
src/constants/adminTypeConstants.js Vedi File

@@ -0,0 +1,7 @@
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 Vedi File

@@ -33,4 +33,26 @@ export const sortAdminEnum = {
value: 2,
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 Vedi File

@@ -1,3 +1,4 @@
import { useMemo } from "react";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
@@ -9,13 +10,16 @@ import { sortEnum } from "../../enums/sortEnum";
import { setFilteredSortOption } from "../../store/actions/filters/filtersActions";
import { selectSelectedSortOption } from "../../store/selectors/filtersSelectors";

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

// On every change of sorting option, new request to backend should be sent
useEffect(() => {
@@ -60,6 +64,7 @@ const useSorting = (applyAllFilters) => {
changeSortingFromName,
apply,
clear,
sortOptions
};
};
export default useSorting;

+ 2
- 1
src/i18n/resources/rs.js Vedi File

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

+ 2
- 2
src/layouts/ItemDetailsLayout/ItemDetailsLayout.js Vedi File

@@ -15,8 +15,8 @@ const ItemDetailsLayout = (props) => {
>
{props.children}
<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}
</RightCard>
</ContentRightCardContainer>

+ 1
- 1
src/layouts/ItemDetailsLayout/ItemDetailsLayout.styled.js Vedi File

@@ -5,7 +5,7 @@ export const ItemDetailsLayoutContainer = styled(Container)`
/* padding-left: 36px; */
/* padding-right: ${(props) => (props.singleOffer ? "76px" : 0)}; */
margin: 0;
width: 100vw;
width: calc(100vw - 17px);
max-width: 100vw;
/* display: flex; */
position: relative;

+ 11
- 6
src/pages/AdminHomePage/AdminCategoriesPage/AdminCategoriesPage.js Vedi File

@@ -19,15 +19,23 @@ import { setManualSearchString } from "../../../store/actions/filters/filtersAct
import selectedTheme from "../../../themes";
import { useState } from "react";
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 { t } = useTranslation();
const dispatch = useDispatch();
const sorting = useSorting(() => {}, sortCategoriesEnum);
const categories = useSelector(selectCategories);
const manualSearchString = useSelector(selectManualSearchString);
const [openedAddModal, setOpenedAddModal] = useState(false);
useEffect(() => {
dispatch(fetchCategories());
return () => {
dispatch(setManualSearchString(""));
sorting.clear();
};
}, []);
const handleSearch = (value) => {
console.log(value);
@@ -35,14 +43,10 @@ const AdminCategoriesPage = () => {
};
const categoriesToShow = useMemo(() => {
if (categories) {
if (manualSearchString)
return categories.filter((item) =>
item.name.toLowerCase().includes(manualSearchString.toLowerCase())
);
return categories;
return adminSortMethod(categories, manualSearchString, sorting);
}
return [];
}, [categories, manualSearchString]);
}, [categories, manualSearchString, sorting.selectedSortOptionLocally]);
return (
<>
<AdminCategoriesPageContainer>
@@ -56,6 +60,7 @@ const AdminCategoriesPage = () => {
categories
hideGrid
isAdmin
sorting={sorting}
hideBackButton
/>
<CategoriesList>

+ 12
- 6
src/pages/AdminHomePage/AdminLocationsPage/AdminLocationsPage.js Vedi File

@@ -16,15 +16,23 @@ import selectedTheme from "../../../themes";
import EditCategory from "../../../components/Modals/EditCategory/EditCategory";
import { setManualSearchString } from "../../../store/actions/filters/filtersActions";
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 [openedAddModal, setOpenedAddModal] = useState(false);
const { t } = useTranslation();
const dispatch = useDispatch();
const sorting = useSorting(() => {}, sortCategoriesEnum);
const locations = useSelector(selectLocations);
const manualSearchString = useSelector(selectManualSearchString);
useEffect(() => {
dispatch(fetchLocations());
return () => {
dispatch(setManualSearchString(""));
sorting.clear();
};
}, []);
const handleSearch = (value) => {
console.log(value);
@@ -32,13 +40,9 @@ const AdminLocationsPage = () => {
};
const locationsToShow = useMemo(() => {
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 (
<>
<AdminLocationsPageContainer>
@@ -53,10 +57,12 @@ const AdminLocationsPage = () => {
hideGrid
isAdmin
hideBackButton
sorting={sorting}
/>
<LocationsList>
{locationsToShow.map((category) => (
<CategoryCard
dontNavigate
key={category._id}
category={category}
type="locations"

+ 29
- 13
src/pages/AdminHomePage/AdminSubcategoriesPage/AdminSubcategoriesPage.js Vedi File

@@ -9,6 +9,8 @@ import {
AdminSubcategoriesHeader,
AdminSubcategoriesPageContainer,
AdminSubcategoriesSearchField,
ButtonContainer,
HeaderText,
NewSubcategoryButton,
SponsoredCategoryCard,
SubcategoriesList,
@@ -21,17 +23,24 @@ import CategoryCard from "../../../components/Cards/CategoryCard/CategoryCard";
import selectedTheme from "../../../themes";
import { useState } from "react";
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 { t } = useTranslation();
const dispatch = useDispatch();
const categories = useSelector(selectCategories);
const routeMatch = useRouteMatch();
const sorting = useSorting(() => {}, sortCategoriesEnum);
const manualSearchString = useSelector(selectManualSearchString);
const [openedAddModal, setOpenedAddModal] = useState(false);

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

const handleSearch = (value) => {
@@ -49,18 +58,19 @@ const AdminSubcategoriesPage = () => {

const subcategories = useMemo(() => {
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 [];
}, [category, manualSearchString]);
}, [category, manualSearchString, sorting.selectedSortOptionLocally]);
console.log("subc", categories);
const goBack = () => {
history.goBack();
};

return (
<>
<AdminSubcategoriesPageContainer>
@@ -69,6 +79,10 @@ const AdminSubcategoriesPage = () => {
handleSearch={handleSearch}
placeholder={t("admin.subcategories.placeholder")}
/>
<ButtonContainer onClick={goBack}>
<ArrowButton side={"left"}></ArrowButton>
<HeaderText>{t("admin.subcategories.goBack")}</HeaderText>
</ButtonContainer>
<AdminSubcategoriesHeader
hideSorting
myOffers
@@ -87,7 +101,7 @@ const AdminSubcategoriesPage = () => {
/>
</SubcategoriesList>
<AdminSubcategoriesHeader
hideSorting
sorting={sorting}
myOffers
subcategories
hideGrid
@@ -95,11 +109,12 @@ const AdminSubcategoriesPage = () => {
hideBackButton
/>
<SubcategoriesList>
{subcategories.map((category) => (
{subcategories.map((subcategory) => (
<CategoryCard
categoryId={category._id}
subcategory
key={category._id}
category={category}
key={subcategory._id}
category={subcategory}
type="subcategories"
hideSecondLabel
hideCheckButton
@@ -118,6 +133,7 @@ const AdminSubcategoriesPage = () => {
{openedAddModal && (
<EditCategory
hideImagePicker
categoryId={category._id}
setOpenedEditModal={setOpenedAddModal}
type="subcategories"
method="add"

+ 33
- 4
src/pages/AdminHomePage/AdminSubcategoriesPage/AdminSubcategoriesPage.styled.js Vedi File

@@ -1,4 +1,4 @@
import { Box } from "@mui/material";
import { Box, Link, Typography } from "@mui/material";
import styled from "styled-components";
import { PrimaryButton } from "../../../components/Buttons/PrimaryButton/PrimaryButton";
import CategoryCard from "../../../components/Cards/CategoryCard/CategoryCard";
@@ -24,12 +24,19 @@ export const AdminSubcategoriesPageContainer = styled(Box)`
export const AdminSubcategoriesHeader = styled(Header)`
top: 4px;
@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;
& div {
& > div:nth-child(1) {
margin-top: 10px;
}
& div div:nth-child(1) {
& div div:nth-child(2) {
top: 22px;
}
}
@@ -42,6 +49,11 @@ export const AdminSubcategoriesSearchField = styled(SearchField)`
`;
export const SubcategoriesList = styled(Box)`
padding-top: 18px;
@media (max-width: 600px) {
&:nth-child(4) {
padding-top: 0;
}
}
`
export const SponsoredCategoryCard = styled(CategoryCard)`
background: ${selectedTheme.colors.backgroundSponsoredColor};
@@ -61,3 +73,20 @@ export const NewSubcategoryButton = styled(PrimaryButton)`
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 Vedi File

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

const handleSearch = () => {};
@@ -32,10 +32,11 @@ const AdminUsersPage = () => {
<AdminUsersSearchField
isAdmin
handleSearch={handleSearch}
placeholder={t("admin.subcategories.placeholder")}
placeholder={t("admin.users.searchPlaceholder")}
/>
<AdminUsersHeader
myOffers
hideSorting
category
hideGrid
isAdmin

+ 21
- 0
src/request/apiEndpoints.js Vedi File

@@ -121,6 +121,10 @@ export default {
getProfile: "users/",
editProfile: "users/{userId}",
getAllProfiles: "users/companies",
editProfileAsAdmin: "admin/users/{userId}",
getAllProfilesAsAdmin: "admin/users",
deleteProfileAsAdmin: "admin/users/{userId}",
blockProfileAsAdmin: "admin/users/{userId}/block",
},
applications: {
application: "/applications/{applicationUid}",
@@ -188,4 +192,21 @@ export default {
postReview: "/users/{userId}/reviews",
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 Vedi File

@@ -1,5 +1,52 @@
import { getRequest } from ".";
import {
deleteRequest,
getRequest,
postRequest,
putRequest,
replaceInUrl,
} from ".";
import apiEndpoints from "./apiEndpoints";

export const attemptFetchCategories = () =>
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 Vedi File

@@ -1,5 +1,28 @@
import { getRequest } from ".";
import {
deleteRequest,
getRequest,
postRequest,
putRequest,
replaceInUrl,
} from ".";
import apiEndpoints from "./apiEndpoints";

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 Vedi File

@@ -1,4 +1,10 @@
import { getRequest, putRequest, replaceInUrl } from ".";
import {
deleteRequest,
getRequest,
patchRequest,
putRequest,
replaceInUrl,
} from ".";
import apiEndpoints from "./apiEndpoints";

export const attemptFetchProfile = (payload) =>
@@ -6,11 +12,29 @@ export const attemptFetchProfile = (payload) =>

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

export const attemptEditProfile = (payload, requestData) => {
// putRequest(apiEndpoints.users.editProfile + "/" + payload, requestData);
return putRequest(
replaceInUrl(apiEndpoints.users.editProfile, { userId: payload }),
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 Vedi File

@@ -0,0 +1,10 @@
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 Vedi File

@@ -0,0 +1,16 @@
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 Vedi File

@@ -16,6 +16,11 @@ export const PROFILE_ALL_FETCH = createFetchType(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_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_FETCH = createFetchType(PROFILE_MINE_SCOPE);
export const PROFILE_MINE_FETCH_SUCCESS = createSuccessType(PROFILE_MINE_SCOPE);
@@ -30,4 +35,9 @@ export const PROFILE_EDIT = createFetchType(PROFILE_EDIT_SCOPE);
export const PROFILE_EDIT_SUCCESS = createSuccessType(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");

+ 32
- 2
src/store/actions/profile/profileActions.js Vedi File

@@ -7,13 +7,19 @@ import {
PROFILE_SET,
PROFILE_SUCCESS,
PROFILE_EDIT,
PROFILE_MINE_FETCH_SUCCESS,
PROFILE_EDIT_SUCCESS,
PROFILE_MINE_FETCH_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_SUCCESS,
PROFILE_ALL_ERROR,
PROFILE_ALL_ADMIN_FETCH,
PROFILE_ALL_ADMIN_SUCCESS,
PROFILE_ALL_ADMIN_ERROR,
PROFILE_ALL_SET,
} from "./profileActionConstants";

@@ -43,6 +49,19 @@ export const fetchAllProfilesError = (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 = () => ({
type: PROFILE_MINE_FETCH,
});
@@ -64,6 +83,17 @@ export const editProfileError = () => ({
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 = () => ({
type: PROFILE_CLEAR,
});

+ 218
- 0
src/store/saga/adminSaga.js Vedi File

@@ -0,0 +1,218 @@
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 Vedi File

@@ -1,4 +1,5 @@
import { all } from 'redux-saga/effects';
import adminSaga from './adminSaga';
import categoriesSaga from './categoriesSaga';
import chatSaga from './chatSaga';
import counterSaga from './counterSaga';
@@ -25,6 +26,7 @@ export default function* rootSaga() {
queryStringSaga(),
exchangeSaga(),
reviewSaga(),
counterSaga()
counterSaga(),
adminSaga()
]);
}

+ 54
- 29
src/store/saga/profileSaga.js Vedi File

@@ -1,7 +1,9 @@
import { all, call, put, takeLatest, select } from "@redux-saga/core/effects";
import {
attemptEditProfile,
attemptEditProfileAsAdmin,
attemptFetchAllProfiles,
attemptFetchAllProfilesAsAdmin,
attemptFetchProfile,
} from "../../request/profileRequest";
import {
@@ -9,10 +11,16 @@ import {
PROFILE_MINE_FETCH,
PROFILE_EDIT,
PROFILE_ALL_FETCH,
PROFILE_EDIT_ADMIN,
PROFILE_ALL_ADMIN_FETCH,
} from "../actions/profile/profileActionConstants";
import {
editProfileAsAdminError,
editProfileAsAdminSuccess,
editProfileError,
editProfileSuccess,
fetchAllProfilesAsAdminError,
fetchAllProfilesAsAdminSuccess,
fetchAllProfilesError,
fetchAllProfilesSuccess,
fetchErrorProfile,
@@ -59,37 +67,19 @@ function* fetchAllProfiles() {
}
}

function* changeMineProfile(payload) {
function* fetchAllProfilesAsAdmin() {
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();
if (typeof payload.payload.firmLogo !== "string")
requestBody.append("file", payload.payload.firmLogo);
@@ -125,11 +115,46 @@ function* changeMineProfile(payload) {
}
}

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() {
yield all([
takeLatest(PROFILE_FETCH, fetchProfile),
takeLatest(PROFILE_MINE_FETCH, fetchMineProfile),
takeLatest(PROFILE_EDIT, changeMineProfile),
takeLatest(PROFILE_ALL_FETCH, fetchAllProfiles),
takeLatest(PROFILE_ALL_ADMIN_FETCH, fetchAllProfilesAsAdmin),
takeLatest(PROFILE_EDIT_ADMIN, changeProfileAsAdmin),
]);
}

+ 42
- 0
src/util/helpers/adminSortHelper.js Vedi File

@@ -0,0 +1,42 @@
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 Vedi File

@@ -15,7 +15,8 @@ export const variants = {
profileCard: "profileCard",
createReviewCard: "createReviewCard",
carousel: "carousel",
adminProfileCard: "reviewCard"
adminProfileCard: "reviewCard",
categoryIcon: "categoryIcon"
};

const cloudFlareVariants = {
@@ -37,6 +38,7 @@ const cloudFlareVariants = {
createReviewCard: "primaryMobile",
carousel: "carousel",
carouselMobile: "carouselMobile",
categoryIcon: "categoryIcon"
};
export const getImageUrl = (imageUrl, variant, isMobile = false) => {
let imageVariant = "";

+ 2
- 1
src/util/helpers/routeHelpers.js Vedi File

@@ -57,7 +57,8 @@ export const isAdminRoute = () => {
dynamicRouteMatches(ADMIN_SUBCATEGORIES_PAGE) ||
routeMatches(ADMIN_LOCATIONS_PAGE) ||
routeMatches(ADMIN_PAYMENT_PAGE) ||
dynamicRouteMatches(ADMIN_SINGLE_USER_PAGE)
dynamicRouteMatches(ADMIN_SINGLE_USER_PAGE) ||
isInRoute(ADMIN_HOME_PAGE)
)
return true;
return false;

Loading…
Annulla
Salva