| @@ -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; | |||
| @@ -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; | |||
| @@ -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; | |||
| `; | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; */ | |||
| @@ -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)` | |||
| @@ -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; | |||
| `; | |||
| @@ -100,6 +100,7 @@ const BigProfileCard = (props) => { | |||
| )} | |||
| {deleteOrEditModal.show && ( | |||
| <DeleteCategory | |||
| customId={props.profile._id} | |||
| setOpenedDeleteModal={closeModalHandler} | |||
| type={deleteOrEditModal.type} | |||
| customLabeledCard={<UserLabeledCard user={props.profile} />} | |||
| @@ -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, | |||
| }) | |||
| ); | |||
| @@ -178,6 +178,7 @@ const ProfileCard = (props) => { | |||
| {editProfileModal && ( | |||
| <EditProfile | |||
| profile={profile} | |||
| isAdmin={props.isAdmin} | |||
| closeModalHandler={closeModalHandler} | |||
| reFetchProfile={reFetchProfile} | |||
| /> | |||
| @@ -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}; | |||
| @@ -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; | |||
| } | |||
| `; | |||
| @@ -16,7 +16,7 @@ const CategoryDetail = (props) => { | |||
| <LocationIcon /> | |||
| </DetailIcon> | |||
| <DetailText ismyprofile={props.isMyProfile}> | |||
| {offer.offer.location.city} | |||
| {offer.offer?.location?.city} | |||
| </DetailText> | |||
| </DetailContainer> | |||
| ); | |||
| @@ -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> | |||
| ); | |||
| })} | |||
| @@ -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; | |||
| @@ -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)` | |||
| @@ -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 = { | |||
| @@ -0,0 +1,3 @@ | |||
| export const EDIT_TYPE = "edit"; | |||
| export const DELETE_TYPE = "delete"; | |||
| export const ADD_TYPE = "add"; | |||
| @@ -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" | |||
| @@ -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" | |||
| } | |||
| } | |||
| @@ -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; | |||
| @@ -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", | |||
| @@ -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> | |||
| @@ -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; | |||
| @@ -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> | |||
| @@ -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" | |||
| @@ -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" | |||
| @@ -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}; | |||
| ` | |||
| @@ -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 | |||
| @@ -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}", | |||
| }, | |||
| }, | |||
| }; | |||
| @@ -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, | |||
| }) | |||
| ); | |||
| @@ -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, | |||
| }) | |||
| ); | |||
| @@ -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, | |||
| }) | |||
| ); | |||
| @@ -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); | |||
| @@ -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, | |||
| }); | |||
| @@ -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"); | |||
| @@ -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, | |||
| }); | |||
| @@ -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)]); | |||
| } | |||
| @@ -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() | |||
| ]); | |||
| } | |||
| @@ -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), | |||
| ]); | |||
| } | |||
| @@ -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; | |||
| }; | |||
| @@ -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 = ""; | |||
| @@ -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; | |||