| @@ -1,4 +1,4 @@ | |||
| import React from "react"; | |||
| import React, { useEffect } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| CategoryCardContainer, | |||
| @@ -16,15 +16,47 @@ import history from "../../../store/utils/history"; | |||
| import { replaceInRoute } from "../../../util/helpers/routeHelpers"; | |||
| import { ADMIN_SUBCATEGORIES_PAGE } from "../../../constants/pages"; | |||
| import { useMemo } from "react"; | |||
| import { useDispatch } from "react-redux"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { | |||
| toggleDeleteCategoryModal, | |||
| toggleEditCategoryModal, | |||
| toggleEditPaymentModal, | |||
| } from "../../../store/actions/modal/modalActions"; | |||
| import { selectAllProfiles } from "../../../store/selectors/profileSelectors"; | |||
| import { fetchAllProfiles } from "../../../store/actions/profile/profileActions"; | |||
| import { selectOffers } from "../../../store/selectors/offersSelectors"; | |||
| import { fetchOffers } from "../../../store/actions/offers/offersActions"; | |||
| import { formatDateLocale } from "../../../util/helpers/dateHelpers"; | |||
| const CategoryCard = (props) => { | |||
| const { t } = useTranslation(); | |||
| const dispatch = useDispatch(); | |||
| const profiles = useSelector(selectAllProfiles); | |||
| const offers = useSelector(selectOffers); | |||
| useEffect(() => { | |||
| dispatch(fetchAllProfiles()); | |||
| dispatch(fetchOffers({ queryString: "" })); | |||
| }, []); | |||
| const company = useMemo(() => { | |||
| if (profiles) { | |||
| return profiles?.filter?.( | |||
| (profile) => profile?._id === props?.category?.user?._id | |||
| ); | |||
| } else { | |||
| return []; | |||
| } | |||
| }, [profiles]); | |||
| const offer = useMemo(() => { | |||
| if (offers) { | |||
| return offers?.filter?.( | |||
| (offer) => offer?._id === props?.category?.offer?._id | |||
| ); | |||
| } else { | |||
| return []; | |||
| } | |||
| }, [offers]); | |||
| const navigateToCategory = () => { | |||
| if (!props.hideCheckButton) { | |||
| history.push( | |||
| @@ -35,53 +67,94 @@ const CategoryCard = (props) => { | |||
| } | |||
| }; | |||
| const showEditCategoryModal = () => { | |||
| dispatch( | |||
| toggleEditCategoryModal({ | |||
| hideImagePicker: props.type !== "categories", | |||
| category: props.category, | |||
| categoryId: props.categoryId, | |||
| subcategory: props.subcategory, | |||
| type: props.type, | |||
| method: "edit", | |||
| firstOutlined: false, | |||
| }) | |||
| ); | |||
| if (!props.payments) { | |||
| dispatch( | |||
| toggleEditCategoryModal({ | |||
| hideImagePicker: props.type !== "categories", | |||
| category: props.category, | |||
| categoryId: props.categoryId, | |||
| subcategory: props.subcategory, | |||
| type: props.type, | |||
| method: "edit", | |||
| firstOutlined: false, | |||
| }) | |||
| ); | |||
| } else { | |||
| dispatch( | |||
| toggleEditPaymentModal({ | |||
| paymentInfo: { | |||
| id: props.category._id, | |||
| payerName: props.category.payerName, | |||
| companyName: company[0].companyName, | |||
| type: props.category.type, | |||
| date: props.category.date, | |||
| offerName: offer[0].name, | |||
| }, | |||
| }) | |||
| ); | |||
| } | |||
| }; | |||
| const showDeleteCategoryModal = () => { | |||
| dispatch( | |||
| toggleDeleteCategoryModal({ | |||
| categoryId: props.categoryId, | |||
| subcategory: props.subcategory, | |||
| category: props.category, | |||
| type: props.type, | |||
| }) | |||
| ); | |||
| if (!props.payments) { | |||
| dispatch( | |||
| toggleDeleteCategoryModal({ | |||
| categoryId: props.categoryId, | |||
| subcategory: props.subcategory, | |||
| category: props.category, | |||
| type: props.type, | |||
| }) | |||
| ); | |||
| } else { | |||
| dispatch( | |||
| toggleDeleteCategoryModal({ | |||
| id: props.category._id, | |||
| category: props.category, | |||
| type: props.type, | |||
| }) | |||
| ); | |||
| } | |||
| }; | |||
| const isDisabledDelete = useMemo(() => { | |||
| return ( | |||
| props?.category?.name === "Ostalo" || props?.category?.city === "Ostalo" | |||
| ); | |||
| }, [props.category]); | |||
| const date = formatDateLocale(new Date(props?.category?.date)); | |||
| return ( | |||
| <> | |||
| <CategoryCardContainer className={props.className}> | |||
| <CategoryCardLeftContainer> | |||
| <CategoryCardName | |||
| image={props?.category?.image} | |||
| categoryName={props?.category?.name || props?.category?.city} | |||
| categoryName={ | |||
| props?.category?.name || | |||
| props?.category?.city || | |||
| props?.category?.payerName | |||
| } | |||
| onClick={navigateToCategory} | |||
| /> | |||
| <CategoryCardDetailsContainer> | |||
| <CategoryDetail | |||
| label={t("admin.categories.noOfOffers")} | |||
| value={props?.category?.offerCount} | |||
| payments={props.payments} | |||
| label={ | |||
| !props.payments | |||
| ? t("admin.categories.noOfOffers") | |||
| : t("admin.payment.typeOfPayment") | |||
| } | |||
| value={props?.category?.offerCount || props?.category?.type} | |||
| /> | |||
| {!props.hideSecondLabel && ( | |||
| <CategoryDetail | |||
| payments={props.payments} | |||
| label={props?.secondLabel} | |||
| value={ | |||
| props?.category?.subcategories?.length || | |||
| props?.category?.offerCount | |||
| !props.payments | |||
| ? props?.category?.subcategories?.length || | |||
| props?.category?.offerCount | |||
| : date | |||
| } | |||
| /> | |||
| )} | |||
| @@ -115,6 +188,7 @@ CategoryCard.propTypes = { | |||
| subcategory: PropTypes.bool, | |||
| type: PropTypes.string, | |||
| categoryId: PropTypes.string, | |||
| payments: PropTypes.bool, | |||
| }; | |||
| export default CategoryCard; | |||
| @@ -8,9 +8,13 @@ import { | |||
| const CategoryDetail = (props) => { | |||
| return ( | |||
| <CategoryDetailContainer> | |||
| <CategoryDetailLabel>{props.label}</CategoryDetailLabel> | |||
| <CategoryDetailValue>{props.value}</CategoryDetailValue> | |||
| <CategoryDetailContainer payments={props.payments}> | |||
| <CategoryDetailLabel payments={props.payments}> | |||
| {props.label} | |||
| </CategoryDetailLabel> | |||
| <CategoryDetailValue payments={props.payments}> | |||
| {props.value} | |||
| </CategoryDetailValue> | |||
| </CategoryDetailContainer> | |||
| ); | |||
| }; | |||
| @@ -18,6 +22,7 @@ const CategoryDetail = (props) => { | |||
| CategoryDetail.propTypes = { | |||
| label: PropTypes.string, | |||
| value: PropTypes.string, | |||
| payments: PropTypes.bool, | |||
| }; | |||
| export default CategoryDetail; | |||
| @@ -8,9 +8,9 @@ export const CategoryDetailContainer = styled(Box)` | |||
| flex-direction: row; */ | |||
| margin-top: 20px; | |||
| margin-bottom: 20px; | |||
| min-width: 150px; | |||
| text-align: center; | |||
| min-width: ${(props) => (props.payments ? "220px" : "150px")}; | |||
| border-left: 1px solid ${hexToRGB(selectedTheme.colors.primaryText, 0.13)}; | |||
| ${(props) => !props.payments && ` text-align: center; `} | |||
| @media (max-width: 600px) { | |||
| margin: 0; | |||
| min-width: 123px; | |||
| @@ -18,7 +18,7 @@ export const CategoryDetailContainer = styled(Box)` | |||
| border-left: 0; | |||
| } | |||
| &:nth-child(2) { | |||
| padding-left: 18px; | |||
| ${(props) => !props.payments && ` padding-left: 18px; `} | |||
| } | |||
| } | |||
| `; | |||
| @@ -29,11 +29,11 @@ export const CategoryDetailLabel = styled(Typography)` | |||
| letter-spacing: 0.01rem; | |||
| padding-top: 14px; | |||
| padding-right: 3px; | |||
| ${(props) => props.payments && ` padding-left: 18px; `} | |||
| @media (max-width: 600px) { | |||
| position: relative; | |||
| bottom: 2.5px; | |||
| padding-top: 0; | |||
| } | |||
| `; | |||
| export const CategoryDetailValue = styled(Typography)` | |||
| @@ -49,5 +49,6 @@ export const CategoryDetailValue = styled(Typography)` | |||
| padding: 0; | |||
| font-size: 12px; | |||
| font-weight: 600; | |||
| ${(props) => props.payments && ` padding-right: 18px; `} | |||
| } | |||
| `; | |||
| @@ -19,8 +19,8 @@ const FilterCard = (props) => { | |||
| const locationRef = useRef(null); | |||
| const companyRef = useRef(null); | |||
| const closeAllSections = () => { | |||
| categoryRef.current.closeSection(); | |||
| subcategoryRef.current.closeSection(); | |||
| categoryRef?.current?.closeSection(); | |||
| subcategoryRef?.current?.closeSection(); | |||
| locationRef?.current?.closeSection(); | |||
| companyRef?.current?.closeSection(); | |||
| }; | |||
| @@ -30,6 +30,7 @@ const FilterCard = (props) => { | |||
| filtersOpened={props.filtersOpened} | |||
| myOffers={props.myOffers} | |||
| skeleton={props.skeleton} | |||
| payments={props.payments} | |||
| > | |||
| {props?.skeleton && <SkeletonFilterCard skeleton={props.skeleton} />} | |||
| {/* Header title for my offers */} | |||
| @@ -40,24 +41,30 @@ const FilterCard = (props) => { | |||
| closeAllSections={closeAllSections} | |||
| isMyOffers={props.myOffers} | |||
| toggleFilters={props.toggleFilters} | |||
| toggleDrawer={props.toggleDrawer} | |||
| payments={props.payments} | |||
| /> | |||
| <ContentContainer> | |||
| {/* Categories */} | |||
| <CategoryChoser filters={filters} ref={categoryRef} offers={offers} /> | |||
| {!props.payments && ( | |||
| <CategoryChoser filters={filters} ref={categoryRef} offers={offers} /> | |||
| )} | |||
| {/* Subcategories */} | |||
| <SubcategoryChoser | |||
| filters={filters} | |||
| queryStringHook={offers.queryStringHook} | |||
| ref={subcategoryRef} | |||
| categoryOpened={categoryRef.current?.isOpened} | |||
| myOffers={props.myOffers} | |||
| offers={offers} | |||
| /> | |||
| {!props.payments && ( | |||
| <SubcategoryChoser | |||
| filters={filters} | |||
| queryStringHook={offers.queryStringHook} | |||
| ref={subcategoryRef} | |||
| categoryOpened={categoryRef.current?.isOpened} | |||
| myOffers={props.myOffers} | |||
| offers={offers} | |||
| /> | |||
| )} | |||
| {/* Locations */} | |||
| {!props.myOffers && ( | |||
| {!props.myOffers && !props.payments && ( | |||
| <LocationChoser filters={filters} ref={locationRef} offers={offers} /> | |||
| )} | |||
| @@ -72,6 +79,8 @@ const FilterCard = (props) => { | |||
| filters={offers} | |||
| closeAllSections={closeAllSections} | |||
| isMyOffers={props.myOffers} | |||
| toggleDrawer={props.toggleDrawer} | |||
| payments={props.payments} | |||
| /> | |||
| </FilterCardContainer> | |||
| ); | |||
| @@ -87,6 +96,8 @@ FilterCard.propTypes = { | |||
| skeleton: PropTypes.bool, | |||
| filtersOpened: PropTypes.bool, | |||
| toggleFilters: PropTypes.func, | |||
| payments: PropTypes.bool, | |||
| toggleDrawer: PropTypes.func, | |||
| }; | |||
| FilterCard.defaultProps = { | |||
| @@ -10,8 +10,9 @@ export const FilterCardContainer = styled(Box)` | |||
| props.myOffers ? `calc(100% - 153px)` : `calc(100% - 90px)`}; | |||
| padding: ${(props) => (props.skeleton ? "0" : "36px")}; | |||
| background-color: white; | |||
| width: calc(100% / 12 * 3.5); | |||
| left: 0; | |||
| width: ${(props) => (!props.payments ? "calc(100% / 12 * 3.5)" : "209px")}; | |||
| ${(props) => !props.payments && `left: 0; margin-top: -24px;`} | |||
| ${(props) => props.payments && "top: 0;"} | |||
| bottom: 0; | |||
| max-width: 360px; | |||
| display: ${(props) => | |||
| @@ -22,7 +23,6 @@ export const FilterCardContainer = styled(Box)` | |||
| min-width: fit-content; | |||
| min-width: 285px !important; | |||
| z-index: 9; | |||
| margin-top: -24px; | |||
| transition: all ease-in-out 1s; | |||
| transition: padding 0s; | |||
| @@ -31,18 +31,18 @@ export const FilterCardContainer = styled(Box)` | |||
| top: -73px; | |||
| } | |||
| @media (max-width: 900px) { | |||
| margin-left: -400px; | |||
| ${(props) => !props.payments && `margin-left: -400px;`} | |||
| ${(props) => | |||
| props.filtersOpened | |||
| ? ` | |||
| props.filtersOpened && | |||
| ` | |||
| display: "flex"; | |||
| margin-left: 0; | |||
| max-width: 100vw; | |||
| width: 100vw; | |||
| bottom: 0; | |||
| height: calc(100% - 50px); | |||
| ` | |||
| : "display: none"}; | |||
| `}; | |||
| transition: all ease-in-out 0.36s; | |||
| } | |||
| & * { | |||
| @@ -17,13 +17,18 @@ const FilterFooter = (props) => { | |||
| // props.toggleFilters(); | |||
| // }; | |||
| const clearFilters = () => { | |||
| if (props.isMyOffers) { | |||
| filters.clear(); | |||
| if (!props.payments) { | |||
| if (props.isMyOffers) { | |||
| filters.clear(); | |||
| } else { | |||
| filters.clearOnlyFiltersAndApply(); | |||
| } | |||
| props.toggleFilters(); | |||
| props.closeAllSections(); | |||
| } else { | |||
| props.toggleDrawer(); | |||
| filters.clearOnlyFiltersAndApply(); | |||
| } | |||
| props.toggleFilters(); | |||
| props.closeAllSections(); | |||
| }; | |||
| return ( | |||
| <FilterFooterContainer responsiveOpen={isMobile}> | |||
| @@ -68,6 +73,8 @@ const FilterFooter = (props) => { | |||
| filters: PropTypes.any, | |||
| isMyOffers: PropTypes.bool, | |||
| closeAllSections: PropTypes.func, | |||
| toggleDrawer: PropTypes.func, | |||
| payments: PropTypes.bool, | |||
| }), | |||
| (FilterFooter.defaultProps = { | |||
| responsiveOpen: false, | |||
| @@ -11,19 +11,26 @@ const FilterHeader = (props) => { | |||
| const { t } = useTranslation(); | |||
| const { isMobile } = useIsMobile(); | |||
| const clearFilters = () => { | |||
| if (props.isMyOffers) { | |||
| filters.clear(); | |||
| if (!props.payments) { | |||
| if (props.isMyOffers) { | |||
| filters.clear(); | |||
| } else { | |||
| filters.clearOnlyFiltersAndApply(); | |||
| } | |||
| props.toggleFilters(); | |||
| props.closeAllSections(); | |||
| } else { | |||
| filters.clearOnlyFiltersAndApply(); | |||
| props.toggleDrawer(); | |||
| } | |||
| props.toggleFilters(); | |||
| props.closeAllSections(); | |||
| }; | |||
| return ( | |||
| <FilterHeaderContainer> | |||
| <Title>{t("filters.title")}</Title> | |||
| {isMobile ? ( | |||
| <CloseIcon onClick={props.toggleFilters} /> | |||
| <CloseIcon | |||
| onClick={props.payments ? props.toggleDrawer : props.toggleFilters} | |||
| /> | |||
| ) : ( | |||
| <Link | |||
| to="#" | |||
| @@ -44,6 +51,8 @@ FilterHeader.propTypes = { | |||
| closeAllSections: PropTypes.func, | |||
| isMyOffers: PropTypes.bool, | |||
| toggleFilters: PropTypes.func, | |||
| toggleDrawer: PropTypes.func, | |||
| payments: PropTypes.bool, | |||
| }; | |||
| export default FilterHeader; | |||
| @@ -5,9 +5,9 @@ import { Drawer } from "@mui/material"; | |||
| const DrawerComponent = ({ open, toggleOpen, content, anchor = "right" }) => ( | |||
| <Drawer | |||
| sx={{ | |||
| minWidth: 250, | |||
| minWidth: 280, | |||
| "& .MuiDrawer-paper": { | |||
| minWidth: 250, | |||
| minWidth: 280, | |||
| }, | |||
| }} | |||
| anchor={anchor} | |||
| @@ -0,0 +1,53 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| // import useIsMobile from "../../../hooks/useIsMobile"; | |||
| // import { Drawer as HeaderDrawer } from "../Drawer/Drawer"; | |||
| import Drawer from "../../../MUI/DrawerComponent"; | |||
| import { useState } from "react"; | |||
| import { forwardRef } from "react"; | |||
| import { useImperativeHandle } from "react"; | |||
| import FilterCard from "../../../Cards/FilterCard/FilterCard"; | |||
| import useOffers from "../../../../hooks/useOffers/useOffers"; | |||
| const DrawerContainer = forwardRef((props, ref) => { | |||
| const [openDrawer, setOpenDrawer] = useState(false); | |||
| const [filtersOpened, setFiltersOpened] = useState(false); | |||
| const offers = useOffers(); | |||
| // const { isMobile } = useIsMobile(); | |||
| const toggleFilters = () => { | |||
| setFiltersOpened((prevFiltersOpened) => !prevFiltersOpened); | |||
| }; | |||
| useImperativeHandle(ref, () => ({ | |||
| handleToggleDrawer, | |||
| })); | |||
| const handleToggleDrawer = () => { | |||
| setOpenDrawer((prevOpenDrawer) => !prevOpenDrawer); | |||
| }; | |||
| // if (!isMobile) return <></>; | |||
| return ( | |||
| <Drawer | |||
| open={openDrawer} | |||
| toggleOpen={handleToggleDrawer} | |||
| content={ | |||
| <FilterCard | |||
| payments | |||
| offers={offers} | |||
| filtersOpened={filtersOpened} | |||
| toggleFilters={toggleFilters} | |||
| toggleDrawer={handleToggleDrawer} | |||
| /> | |||
| } | |||
| /> | |||
| ); | |||
| }); | |||
| DrawerContainer.displayName = "DrawerContainer"; | |||
| DrawerContainer.propTypes = { | |||
| showCreateOfferModal: PropTypes.func, | |||
| }; | |||
| export default DrawerContainer; | |||
| @@ -0,0 +1,9 @@ | |||
| import styled from "styled-components"; | |||
| import { Box } from "@mui/material"; | |||
| export const DrawerContainer = styled(Box)` | |||
| width: 100vw; | |||
| position: relative; | |||
| height: 100%; | |||
| overflow: hidden; | |||
| `; | |||
| @@ -1,12 +1,15 @@ | |||
| import React, { useMemo } from "react"; | |||
| import React, { useMemo, useRef } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| CategoryIcon, | |||
| FilterButtonContainer, | |||
| FilterButtonIcon, | |||
| HeaderContainer, | |||
| HeaderOptions, | |||
| HeaderWrapperContainer, | |||
| LocationIcon, | |||
| PageTitleContainer, | |||
| PaymentsIcon, | |||
| SubcategoryIcon, | |||
| SwapsHeaderIcon, | |||
| SwapsIcon, | |||
| @@ -22,9 +25,12 @@ import useIsTablet from "../../../hooks/useIsTablet"; | |||
| import TooltipHeader from "./TooltipHeader/TooltipHeader"; | |||
| import GridButtons from "./GridButtons/GridButtons"; | |||
| import HeaderSelect from "./HeaderSelect/HeaderSelect"; | |||
| // import DrawerContainer from "../../Header/DrawerContainer/DrawerContainer"; | |||
| // import HeaderTitle from "../../Profile/ProfileOffers/HeaderTitle/HeaderTitle"; | |||
| import Drawer from "./Drawer/Drawer"; | |||
| const Header = (props) => { | |||
| const drawerRef = useRef(null); | |||
| const { t } = useTranslation(); | |||
| const sorting = props?.sorting; | |||
| const { isTablet } = useIsTablet(); | |||
| @@ -53,6 +59,8 @@ const Header = (props) => { | |||
| ? t("admin.subcategories.headerTitle") | |||
| : props.location | |||
| ? t("admin.locations.headerTitle") | |||
| : props.payments | |||
| ? t("admin.payment.headerTitle") | |||
| : !isTablet | |||
| ? t("header.myOffers") | |||
| : props.myOffers | |||
| @@ -68,6 +76,8 @@ const Header = (props) => { | |||
| <SubcategoryIcon /> | |||
| ) : props.location ? ( | |||
| <LocationIcon /> | |||
| ) : props.payments ? ( | |||
| <PaymentsIcon /> | |||
| ) : isTablet ? ( | |||
| <SwapsIcon /> | |||
| ) : ( | |||
| @@ -113,6 +123,13 @@ const Header = (props) => { | |||
| hideSorting={props?.hideSorting} | |||
| /> | |||
| )} | |||
| {props.payments && ( | |||
| <FilterButtonContainer | |||
| onClick={drawerRef.current?.handleToggleDrawer} | |||
| > | |||
| <FilterButtonIcon /> | |||
| </FilterButtonContainer> | |||
| )} | |||
| {/* ^^^^^^ */} | |||
| </HeaderOptions> | |||
| </HeaderContainer> | |||
| @@ -123,6 +140,8 @@ const Header = (props) => { | |||
| </PageTitleContainer> | |||
| )} | |||
| </HeaderWrapperContainer> | |||
| <Drawer ref={drawerRef} /> | |||
| {/* <DrawerContainer ref={drawerRef} /> */} | |||
| </> | |||
| ); | |||
| }; | |||
| @@ -145,6 +164,7 @@ Header.propTypes = { | |||
| subcategories: PropTypes.bool, | |||
| hideSorting: PropTypes.bool, | |||
| location: PropTypes.bool, | |||
| payments: PropTypes.bool, | |||
| }; | |||
| Header.defaultProps = { | |||
| isGrid: false, | |||
| @@ -6,6 +6,8 @@ import { ReactComponent as User } from "../../../assets/images/svg/user.svg"; | |||
| import { ReactComponent as Category } from "../../../assets/images/svg/category.svg"; | |||
| import { ReactComponent as Subcategory } from "../../../assets/images/svg/subcategory.svg"; | |||
| import { ReactComponent as Location } from "../../../assets/images/svg/location.svg"; | |||
| import { ReactComponent as Payment } from "../../../assets/images/svg/dollar-sign.svg"; | |||
| import { ReactComponent as Filter } from "../../../assets/images/svg/filter.svg"; | |||
| export const HeaderWrapperContainer = styled(Box)` | |||
| display: ${(props) => (props.skeleton ? "none" : "block")}; | |||
| @@ -109,3 +111,35 @@ export const LocationIcon = styled(Location)` | |||
| stroke: ${selectedTheme.colors.primaryText}; | |||
| } | |||
| `; | |||
| export const PaymentsIcon = styled(Payment)` | |||
| position: relative; | |||
| top: 3px; | |||
| margin-right: 5px; | |||
| & path { | |||
| stroke: ${selectedTheme.colors.primaryText}; | |||
| } | |||
| `; | |||
| export const FilterButtonContainer = styled(Box)` | |||
| background-color: #e4e4e4; | |||
| border-radius: 100%; | |||
| width: 40px; | |||
| height: 40px; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| margin-left: 18px; | |||
| cursor: pointer; | |||
| @media (max-width: 600px) { | |||
| margin-top: 25px; | |||
| } | |||
| `; | |||
| export const FilterButtonIcon = styled(Filter)` | |||
| width: 20px; | |||
| path { | |||
| stroke-width: 2px; | |||
| } | |||
| `; | |||
| @@ -0,0 +1,271 @@ | |||
| import React, { useEffect } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import BackdropComponent from "../../MUI/BackdropComponent"; | |||
| import { useState } from "react"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { useFormik } from "formik"; | |||
| // import { useMemo } from "react"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| // import { fetchAdminMethod } from "../../../store/actions/admin/adminActions"; | |||
| import { useRef } from "react"; | |||
| import { closeModal } from "../../../store/actions/modal/modalActions"; | |||
| import { | |||
| CreatePaymentContainer, | |||
| CreatePaymentTitle, | |||
| PaymentInputField, | |||
| XIcon, | |||
| FieldLabel, | |||
| InputContainer, | |||
| SaveButton, | |||
| ButtonsContainer, | |||
| StyledWrapper, | |||
| ErrorMessage, | |||
| } from "./CreatePayment.styled"; | |||
| import paymentInitialValues from "../../../initialValues/paymentInitialValues"; | |||
| import { selectAllProfiles } from "../../../store/selectors/profileSelectors"; | |||
| import { fetchAllProfiles } from "../../../store/actions/profile/profileActions"; | |||
| import AutoSuggestTextField from "../../TextFields/AutoSuggestTextField/AutoSuggestTextField"; | |||
| import { InputFieldLabelLocation } from "../../Cards/ProfileCard/EditProfile/LocationField/LocationField.styled"; | |||
| import { | |||
| addPayment, | |||
| editPayment, | |||
| fetchPayments, | |||
| } from "../../../store/actions/payment/paymentActions"; | |||
| import { selectOffers } from "../../../store/selectors/offersSelectors"; | |||
| // import { selectQueryString } from "../../../store/selectors/filtersSelectors"; | |||
| import { fetchOffers } from "../../../store/actions/offers/offersActions"; | |||
| import createPaymentValidation from "../../../validations/createPaymentValidation"; | |||
| const CreatePayment = (props) => { | |||
| const { t } = useTranslation(); | |||
| const dispatch = useDispatch(); | |||
| const [clickedOnNext, setClickedOnNext] = useState(false); | |||
| const [offersToShow, setOffersToShow] = useState([]); | |||
| const [userId, setUserId] = useState(""); | |||
| const [offerId, setOfferId] = useState(""); | |||
| const inputRef = useRef(null); | |||
| // const queryString = useSelector(selectQueryString); | |||
| const profiles = useSelector(selectAllProfiles); | |||
| const offers = useSelector(selectOffers); | |||
| useEffect(() => { | |||
| dispatch(fetchAllProfiles()); | |||
| dispatch(fetchOffers({ queryString: "" })); | |||
| }, []); | |||
| const closeModalHandler = () => { | |||
| dispatch(closeModal()); | |||
| }; | |||
| const userIdHandler = (value) => { | |||
| if (value) { | |||
| let userId = profiles?.filter?.( | |||
| (profile) => profile.companyName === value | |||
| )[0]?._id; | |||
| setUserId(userId); | |||
| let filterdOffers = offers?.filter?.( | |||
| (offer) => offer.user._id === userId | |||
| ); | |||
| setOffersToShow(filterdOffers); | |||
| } | |||
| }; | |||
| const offerIdHandler = (value) => { | |||
| if (value) { | |||
| let offerId = offers?.filter?.((offer) => offer.name === value)[0]?._id; | |||
| setOfferId(offerId); | |||
| } | |||
| }; | |||
| const handleApiResponseSuccess = () => { | |||
| closeModalHandler(); | |||
| dispatch(fetchPayments()); | |||
| }; | |||
| const handleSubmit = (values) => { | |||
| if (!props.editPayment) { | |||
| dispatch( | |||
| addPayment({ | |||
| type: values.type, | |||
| payerName: values.payerName, | |||
| userId: userId, | |||
| offerId: offerId, | |||
| date: values.date, | |||
| handleApiResponseSuccess, | |||
| }) | |||
| ); | |||
| } else { | |||
| const editUserId = profiles?.filter( | |||
| (profile) => profile.companyName === values.companyName | |||
| )[0]._id; | |||
| const editOfferId = offers?.filter( | |||
| (offer) => offer.name === values.offerName | |||
| )[0]._id; | |||
| dispatch( | |||
| editPayment({ | |||
| id: props.paymentInfo.id, | |||
| type: values.type, | |||
| payerName: values.payerName, | |||
| userId: editUserId, | |||
| offerId: editOfferId, | |||
| date: values.date, | |||
| handleApiResponseSuccess, | |||
| }) | |||
| ); | |||
| } | |||
| }; | |||
| const formik = useFormik({ | |||
| initialValues: paymentInitialValues(props.paymentInfo), | |||
| validationSchema: createPaymentValidation(profiles, offers), | |||
| onSubmit: handleSubmit, | |||
| }); | |||
| const handleClick = (next) => { | |||
| if (next !== clickedOnNext) setClickedOnNext(next); | |||
| formik.handleSubmit(); | |||
| }; | |||
| return ( | |||
| <> | |||
| <BackdropComponent | |||
| isLoading | |||
| handleClose={closeModalHandler} | |||
| position="fixed" | |||
| /> | |||
| <CreatePaymentContainer hideImagePicker={props.hideImagePicker}> | |||
| <XIcon onClick={closeModalHandler} /> | |||
| <CreatePaymentTitle> | |||
| {!props.editPayment | |||
| ? t("admin.payment.modal.newTitle") | |||
| : t("admin.payment.modal.editTitle")} | |||
| </CreatePaymentTitle> | |||
| <InputContainer hideImagePicker={props.hideImagePicker}> | |||
| <FieldLabel leftText={t("admin.payment.modal.name")} /> | |||
| <PaymentInputField | |||
| name="payerName" | |||
| ref={inputRef} | |||
| // placeholder={placeholder} | |||
| italicPlaceholder | |||
| margin="normal" | |||
| value={formik.values.payerName} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.payerName && formik.errors.payerName} | |||
| helperText={formik.touched.payerName && formik.errors.payerName} | |||
| autoFocus | |||
| fullWidth | |||
| /> | |||
| <InputFieldLabelLocation | |||
| leftText={t("admin.payment.modal.company").toUpperCase()} | |||
| /> | |||
| <AutoSuggestTextField | |||
| data={profiles?.map((item) => ({ | |||
| name: item.companyName, | |||
| }))} | |||
| value={formik.values.companyName} | |||
| error={formik.errors.companyName} | |||
| onChange={(event, { newValue }) => { | |||
| formik.setFieldValue("companyName", newValue); | |||
| userIdHandler(newValue); | |||
| if (newValue === "") { | |||
| formik.setFieldValue("offerName", ""); | |||
| setOffersToShow([]); | |||
| } | |||
| }} | |||
| /> | |||
| <InputFieldLabelLocation | |||
| leftText={t("admin.payment.modal.offer").toUpperCase()} | |||
| /> | |||
| <StyledWrapper> | |||
| <AutoSuggestTextField | |||
| data={ | |||
| userId | |||
| ? offersToShow?.map((item) => ({ | |||
| name: item.name, | |||
| })) | |||
| : [] | |||
| } | |||
| value={formik.values.offerName} | |||
| error={formik.errors.offerName} | |||
| onChange={(event, { newValue }) => { | |||
| formik.setFieldValue("offerName", newValue); | |||
| offerIdHandler(newValue); | |||
| }} | |||
| /> | |||
| </StyledWrapper> | |||
| <FieldLabel leftText={t("admin.payment.modal.type")} /> | |||
| <PaymentInputField | |||
| name="type" | |||
| ref={inputRef} | |||
| // placeholder={placeholder} | |||
| italicPlaceholder | |||
| margin="normal" | |||
| value={formik.values.type} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.type && formik.errors.type} | |||
| helperText={formik.touched.type && formik.errors.type} | |||
| fullWidth | |||
| /> | |||
| <FieldLabel leftText={t("admin.payment.modal.date")} /> | |||
| <PaymentInputField | |||
| name="date" | |||
| ref={inputRef} | |||
| type="date" | |||
| italicPlaceholder | |||
| margin="normal" | |||
| value={formik.values.date} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.date && formik.errors.date} | |||
| helperText={formik.touched.date && formik.errors.date} | |||
| fullWidth | |||
| /> | |||
| {formik.errors.payerName && formik.touched.payerName ? ( | |||
| <ErrorMessage>{formik.errors.payerName}</ErrorMessage> | |||
| ) : formik.errors.companyName && formik.touched.companyName ? ( | |||
| <ErrorMessage>{formik.errors.companyName}</ErrorMessage> | |||
| ) : formik.errors.offerName && formik.touched.offerName ? ( | |||
| <ErrorMessage>{formik.errors.offerName}</ErrorMessage> | |||
| ) : formik.errors.type && formik.touched.type ? ( | |||
| <ErrorMessage>{formik.errors.type}</ErrorMessage> | |||
| ) : formik.errors.date && formik.touched.date ? ( | |||
| <ErrorMessage>{formik.errors.date}</ErrorMessage> | |||
| ) : ( | |||
| <></> | |||
| )} | |||
| </InputContainer> | |||
| <ButtonsContainer> | |||
| <SaveButton | |||
| showSecondButton={props.showSecondButton} | |||
| variant="contained" | |||
| height="48px" | |||
| onClick={() => handleClick(true)} | |||
| > | |||
| {!props.editPayment | |||
| ? t("admin.payment.modal.add") | |||
| : t("admin.payment.modal.change")} | |||
| </SaveButton> | |||
| </ButtonsContainer> | |||
| </CreatePaymentContainer> | |||
| </> | |||
| ); | |||
| }; | |||
| CreatePayment.propTypes = { | |||
| category: PropTypes.any, | |||
| setOpenedEditModal: PropTypes.func, | |||
| hideImagePicker: PropTypes.bool, | |||
| type: PropTypes.string, | |||
| showSecondButton: PropTypes.bool, | |||
| method: PropTypes.string, | |||
| firstOutlined: PropTypes.bool, | |||
| secondOutlined: PropTypes.bool, | |||
| categoryId: PropTypes.string, | |||
| editPayment: PropTypes.bool, | |||
| paymentInfo: PropTypes.any, | |||
| id: PropTypes.string, | |||
| }; | |||
| CreatePayment.defaultProps = { | |||
| firstOutlined: true, | |||
| }; | |||
| export default CreatePayment; | |||
| @@ -0,0 +1,155 @@ | |||
| import { Box, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton"; | |||
| import { Label } from "../../CheckBox/Label"; | |||
| import { TextField } from "../../TextFields/TextField/TextField"; | |||
| import { ReactComponent as X } from "../../../assets/images/svg/plus.svg"; | |||
| export const CreatePaymentContainer = styled(Box)` | |||
| position: fixed; | |||
| width: 623px; | |||
| top: calc(50vh - 326.5px); | |||
| /* height: ${(props) => (props.hideImagePicker ? "296px" : "510px")}; */ | |||
| /* top: ${(props) => | |||
| props.hideImagePicker ? "calc(50vh - 148px)" : "calc(50vh - 146.5px)"}; */ | |||
| left: calc(50vw - 311px); | |||
| background: white; | |||
| z-index: 150; | |||
| & > * { | |||
| margin-left: auto; | |||
| margin-right: auto; | |||
| } | |||
| @media (max-width: 600px) { | |||
| height: 100vh; | |||
| max-height: 100vh; | |||
| min-height: 90vh; | |||
| width: 100vw; | |||
| top: 0; | |||
| left: 0; | |||
| padding: 0 18px; | |||
| } | |||
| `; | |||
| export const CreatePaymentTitle = styled(Typography)` | |||
| display: block; | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-weight: 700; | |||
| font-size: 24px; | |||
| line-height: 31px; | |||
| text-align: center; | |||
| margin-top: 36px; | |||
| width: 256px; | |||
| color: ${selectedTheme.colors.primaryPurple}; | |||
| @media (max-width: 600px) { | |||
| font-size: 18px; | |||
| line-height: 24px; | |||
| margin-top: 18px; | |||
| } | |||
| `; | |||
| export const PaymentInputField = styled(TextField)` | |||
| width: 375px; | |||
| /* margin-bottom: 36px; */ | |||
| @media (max-width: 600px) { | |||
| margin-bottom: 0; | |||
| & div div input { | |||
| font-size: 12px !important; | |||
| } | |||
| & div { | |||
| height: 40px; | |||
| width: 314px; | |||
| } | |||
| } | |||
| `; | |||
| export const FieldLabel = styled(Label)` | |||
| position: relative; | |||
| bottom: -14px; | |||
| width: 376px; | |||
| & label { | |||
| font-size: 12px; | |||
| font-weight: 600; | |||
| line-height: 20px; | |||
| color: ${selectedTheme.colors.primaryGrayText}; | |||
| cursor: auto; | |||
| letter-spacing: 0.2px; | |||
| } | |||
| @media (max-width: 600px) { | |||
| & label { | |||
| font-size: 9px; | |||
| } | |||
| } | |||
| `; | |||
| export const InputContainer = styled(Box)` | |||
| display: block; | |||
| width: fit-content; | |||
| margin-top: 22px; | |||
| @media (max-width: 600px) { | |||
| width: 314px; | |||
| height: 56px; | |||
| margin-top: 27px; | |||
| position: relative; | |||
| top: -18px; | |||
| ${(props) => | |||
| !props.hideImagePicker && | |||
| ` | |||
| margin-top: 25px; | |||
| `} | |||
| } | |||
| `; | |||
| export const SaveButton = styled(PrimaryButton)` | |||
| max-width: 376px; | |||
| width: ${(props) => (props.showSecondButton ? "180px" : "376px")}; | |||
| height: 48px; | |||
| & button { | |||
| letter-spacing: 1.5px; | |||
| } | |||
| @media (max-width: 600px) { | |||
| height: 45px; | |||
| width: ${(props) => (props.showSecondButton ? "149px" : "314px")}; | |||
| } | |||
| `; | |||
| export const XIcon = styled(X)` | |||
| transform: rotate(45deg); | |||
| position: absolute; | |||
| top: 36px; | |||
| right: 36px; | |||
| cursor: pointer; | |||
| width: 26px; | |||
| height: 26px; | |||
| & path { | |||
| stroke: ${selectedTheme.colors.messageText}; | |||
| stroke-width: 2; | |||
| } | |||
| @media (max-width: 600px) { | |||
| top: 18px; | |||
| right: 18px; | |||
| } | |||
| `; | |||
| export const ButtonsContainer = styled(Box)` | |||
| display: flex; | |||
| justify-content: center; | |||
| flex-direction: row; | |||
| gap: 18px; | |||
| margin: 36px auto; | |||
| @media (max-width: 600px) { | |||
| position: absolute; | |||
| bottom: 18px; | |||
| margin: auto; | |||
| left: 28px; | |||
| } | |||
| `; | |||
| export const StyledWrapper = styled(Box)` | |||
| & .react-autosuggest__container { | |||
| z-index: 10; | |||
| } | |||
| `; | |||
| export const ErrorMessage = styled(Typography)` | |||
| color: red; | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-size: 14px; | |||
| `; | |||
| @@ -21,6 +21,10 @@ import { dynamicRouteMatches } from "../../../util/helpers/routeHelpers"; | |||
| import { ADMIN_SINGLE_USER_PAGE } from "../../../constants/pages"; | |||
| import { fetchProfile } from "../../../store/actions/profile/profileActions"; | |||
| import { closeModal } from "../../../store/actions/modal/modalActions"; | |||
| import { | |||
| deletePayment, | |||
| fetchPayments, | |||
| } from "../../../store/actions/payment/paymentActions"; | |||
| const DeleteCategory = (props) => { | |||
| const dispatch = useDispatch(); | |||
| @@ -38,18 +42,31 @@ const DeleteCategory = (props) => { | |||
| } | |||
| closeModalHandler(); | |||
| }; | |||
| const handleApiDeletePaymentResponseSuccess = () => { | |||
| closeModalHandler(); | |||
| dispatch(fetchPayments()); | |||
| }; | |||
| console.log(props); | |||
| const handleSubmit = () => { | |||
| dispatch( | |||
| fetchAdminMethod({ | |||
| type: props.type, | |||
| method: DELETE_TYPE, | |||
| name: props.category?.name, | |||
| id: props.customId || props.category?._id, | |||
| categoryId: props.categoryId, | |||
| handleApiResponseSuccess, | |||
| }) | |||
| ); | |||
| if (!props.type === "payment") { | |||
| dispatch( | |||
| fetchAdminMethod({ | |||
| type: props.type, | |||
| method: DELETE_TYPE, | |||
| name: props.category?.name, | |||
| id: props.customId || props.category?._id, | |||
| categoryId: props.categoryId, | |||
| handleApiResponseSuccess, | |||
| }) | |||
| ); | |||
| } else { | |||
| dispatch( | |||
| deletePayment({ | |||
| id: props.id, | |||
| handleApiDeletePaymentResponseSuccess, | |||
| }) | |||
| ); | |||
| } | |||
| }; | |||
| return ( | |||
| <> | |||
| @@ -66,7 +83,9 @@ const DeleteCategory = (props) => { | |||
| > | |||
| {props.customLabeledCard || ( | |||
| <CategoryName> | |||
| {props.category?.name || props.category?.city} | |||
| {props.category?.name || | |||
| props.category?.city || | |||
| props.category.payerName} | |||
| </CategoryName> | |||
| )} | |||
| </LabeledCard> | |||
| @@ -102,6 +121,7 @@ DeleteCategory.propTypes = { | |||
| customLabeledCardIcon: PropTypes.node, | |||
| categoryId: PropTypes.string, | |||
| customId: PropTypes.string, | |||
| id: PropTypes.string, | |||
| }; | |||
| export default DeleteCategory; | |||
| @@ -9,6 +9,7 @@ import CreateReview from "../CreateReview/CreateReview"; | |||
| import EditProfile from "../Cards/ProfileCard/EditProfile/EditProfile"; | |||
| import DeleteReview from "./DeleteReview/DeleteReview"; | |||
| import DeleteOffer from "../Cards/OfferCard/DeleteOffer/DeleteOffer"; | |||
| import CreatePayment from "./CreatePayment/CreatePayment"; | |||
| const Modal = () => { | |||
| const modals = useSelector(selectModalValues); | |||
| @@ -36,6 +37,10 @@ const Modal = () => { | |||
| {modals?.editCategoryModal && ( | |||
| <EditCategory method="edit" {...modals?.props} /> | |||
| )} | |||
| {modals?.createPaymentModal && <CreatePayment {...modals?.props} />} | |||
| {modals?.editPaymentModal && ( | |||
| <CreatePayment editPayment {...modals?.props} /> | |||
| )} | |||
| {modals?.deleteCategoryModal && <DeleteCategory {...modals?.props} />} | |||
| {modals?.createReviewModal && <CreateReview {...modals?.props} />} | |||
| {modals?.deleteReviewModal && <DeleteReview {...modals?.props} />} | |||
| @@ -37,6 +37,7 @@ const UserReviews = (props) => { | |||
| const [positiveReviews, setPositiveReviews] = useState(false); | |||
| const [negativeReviews, setNegativeReviews] = useState(false); | |||
| const [filteredReviews, setFilteredReviews] = useState(); | |||
| const [recivedReviews, setRecivedReviews] = useState(); | |||
| const { t } = useTranslation(); | |||
| const offer = useSelector(selectOffer); | |||
| const reviews = useSelector(selectSelectedReviews); | |||
| @@ -50,24 +51,38 @@ const UserReviews = (props) => { | |||
| props.givingReview ? ONE_OFFER_SCOPE : REVIEW_GET_SCOPE | |||
| ) | |||
| ); | |||
| useEffect(() => { | |||
| setRecivedReviews( | |||
| reviews.filter( | |||
| (review) => | |||
| review.exchange.buyer.user._id !== routeMatch.params.profileId | |||
| ) | |||
| ); | |||
| }, [reviews]); | |||
| useEffect(() => { | |||
| if (!positiveReviews && !negativeReviews) setFilteredReviews(reviews); | |||
| if (!positiveReviews && !negativeReviews) | |||
| setFilteredReviews(recivedReviews); | |||
| if (positiveReviews) | |||
| setFilteredReviews( | |||
| reviews.filter( | |||
| (review) => | |||
| review.succeeded === reviewEnum.YES.backendText && | |||
| review.communication === reviewEnum.YES.backendTextSecond | |||
| ) | |||
| recivedReviews | |||
| .filter( | |||
| (review) => | |||
| review.exchange.buyer.user._id !== routeMatch.params.profileId | |||
| ) | |||
| .filter( | |||
| (review) => | |||
| review.succeeded === reviewEnum.YES.backendText && | |||
| review.communication === reviewEnum.YES.backendTextSecond | |||
| ) | |||
| ); | |||
| if (negativeReviews) | |||
| setFilteredReviews( | |||
| reviews.filter( | |||
| recivedReviews.filter( | |||
| (review) => review.succeeded === reviewEnum.NO.backendText | |||
| ) | |||
| ); | |||
| }, [reviews, positiveReviews, negativeReviews]); | |||
| }, [recivedReviews, positiveReviews, negativeReviews]); | |||
| const userId = useMemo(() => { | |||
| if ( | |||
| @@ -88,7 +103,6 @@ const UserReviews = (props) => { | |||
| } | |||
| }, [props.givingReview, userId]); | |||
| console.log(props); | |||
| const lastThreeReviews = useMemo(() => { | |||
| console.log("profile Reviews", reviews); | |||
| if (props.givingReview) return [props.givingReview]; | |||
| @@ -190,7 +204,25 @@ const UserReviews = (props) => { | |||
| </ReviewsHeader> | |||
| )} | |||
| <ReviewList ref={listRef} isProfileReviews={props.isProfileReviews}> | |||
| {filteredReviews?.length > 0 ? ( | |||
| {dynamicRouteMatches(ADMIN_SINGLE_USER_PAGE) ? ( | |||
| lastThreeReviews?.length > 0 ? ( | |||
| lastThreeReviews?.map((review, index) => ( | |||
| <UserReviewsCard | |||
| showRemoveIcon={props.isAdmin} | |||
| review={review} | |||
| key={index} | |||
| isProfileReviews={props?.isProfileReviews} | |||
| userId={userId} | |||
| isAdmin={props.isAdmin} | |||
| hasGivenReview={isGiven} | |||
| givingReview={props.givingReview} | |||
| rightReviews={props.rightReviews} | |||
| /> | |||
| )) | |||
| ) : ( | |||
| <NoReviews fullHeight={props.fullHeight} /> | |||
| ) | |||
| ) : filteredReviews?.length > 0 ? ( | |||
| filteredReviews?.map((review, index) => ( | |||
| <UserReviewsCard | |||
| showRemoveIcon={props.isAdmin} | |||
| @@ -1,75 +1,92 @@ | |||
| export const sortEnum = { | |||
| INITIAL: { | |||
| value: 0, | |||
| mainText: "Sortiraj po", | |||
| queryString: "" | |||
| }, | |||
| POPULAR: { | |||
| value: 1, | |||
| mainText: "Najpopularnije", | |||
| queryString: "popular" | |||
| }, | |||
| NEW: { | |||
| value: 2, | |||
| mainText: "Najnovije", | |||
| queryString: "newest" | |||
| }, | |||
| OLD: { | |||
| value: 3, | |||
| mainText: "Najstarije", | |||
| queryString: "oldest" | |||
| } | |||
| } | |||
| INITIAL: { | |||
| value: 0, | |||
| mainText: "Sortiraj po", | |||
| queryString: "", | |||
| }, | |||
| POPULAR: { | |||
| value: 1, | |||
| mainText: "Najpopularnije", | |||
| queryString: "popular", | |||
| }, | |||
| NEW: { | |||
| value: 2, | |||
| mainText: "Najnovije", | |||
| queryString: "newest", | |||
| }, | |||
| OLD: { | |||
| value: 3, | |||
| mainText: "Najstarije", | |||
| queryString: "oldest", | |||
| }, | |||
| }; | |||
| export const sortAdminEnum = { | |||
| INITIAL: { | |||
| value: 0, | |||
| mainText: "Sortiraj po" | |||
| }, | |||
| GIVEN: { | |||
| value: 1, | |||
| mainText: "Date" | |||
| }, | |||
| RECIEVED: { | |||
| value: 2, | |||
| mainText: "Dobijene" | |||
| } | |||
| } | |||
| INITIAL: { | |||
| value: 0, | |||
| mainText: "Sortiraj po", | |||
| }, | |||
| GIVEN: { | |||
| value: 1, | |||
| mainText: "Date", | |||
| }, | |||
| RECIEVED: { | |||
| 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" | |||
| } | |||
| } | |||
| 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", | |||
| }, | |||
| }; | |||
| export const sortUsersEnum = { | |||
| INITIAL: { | |||
| value: 0, | |||
| mainText: "Sortiraj po", | |||
| queryString: "" | |||
| }, | |||
| NEW: { | |||
| value: 1, | |||
| mainText: "Najskorije registrovan", | |||
| queryString: "newest" | |||
| }, | |||
| OLD: { | |||
| value: 2, | |||
| mainText: "Najstarije registrovan", | |||
| queryString: "oldest" | |||
| } | |||
| } | |||
| INITIAL: { | |||
| value: 0, | |||
| mainText: "Sortiraj po", | |||
| queryString: "", | |||
| }, | |||
| NEW: { | |||
| value: 1, | |||
| mainText: "Najskorije registrovan", | |||
| queryString: "newest", | |||
| }, | |||
| OLD: { | |||
| value: 2, | |||
| mainText: "Najstarije registrovan", | |||
| queryString: "oldest", | |||
| }, | |||
| }; | |||
| export const sortPaymentsEnum = { | |||
| INITIAL: { | |||
| value: 0, | |||
| mainText: "Sortiraj po", | |||
| queryString: "", | |||
| }, | |||
| NEW: { | |||
| value: 1, | |||
| mainText: "Najskorije", | |||
| queryString: "newest", | |||
| }, | |||
| OLD: { | |||
| value: 2, | |||
| mainText: "Najstarije", | |||
| queryString: "oldest", | |||
| }, | |||
| }; | |||
| @@ -140,17 +140,17 @@ const useOffers = () => { | |||
| const applyFilters = () => { | |||
| setFiltersCleared(true); | |||
| }; | |||
| const clearFiltersAndApply = () => { | |||
| clear(); | |||
| setFiltersCleared(true); | |||
| }; | |||
| const applySorting = () => { | |||
| paging.changePage(1); | |||
| setFiltersCleared(true); | |||
| }; | |||
| const applySearch = () => { | |||
| paging.changePage(1); | |||
| setFiltersCleared(true); | |||
| @@ -563,6 +563,35 @@ export default { | |||
| next: "Sledeća", | |||
| }, | |||
| }, | |||
| payment: { | |||
| headerTitle: "Uplate", | |||
| placeholder: "Pretražite uplate...", | |||
| typeOfPayment: "Tip: ", | |||
| date: "Datum: ", | |||
| addPayment: "Dodaj uplatu", | |||
| reassuranceDelete: | |||
| "Da li ste sigurni da želite da obrišete odabranu uplatu?", | |||
| cancel: "Otkaži", | |||
| delete: "Obriši", | |||
| modal: { | |||
| newTitle: "Nova Uplata", | |||
| editTitle: "Izmena Uplate", | |||
| name: "UPLATIOC", | |||
| company: "IME KOMPANIJE", | |||
| type: "TIP", | |||
| date: "DATUM", | |||
| offer: "PROIZVOD", | |||
| add: "Dodaj", | |||
| change: "Izmeni", | |||
| }, | |||
| errors: { | |||
| nameRequired: "Ime uplatioca je obavezno!", | |||
| companyRequired: "Ime kompanije je obavezno!", | |||
| offerRequired: "Proizvod je obavezan!", | |||
| typeRequired: "Tip je obavezan!", | |||
| dateRequired: "Datum je obavezan!", | |||
| }, | |||
| }, | |||
| deleteUser: { | |||
| reassuranceDelete: | |||
| "Da li ste sigurni da želite da obrišete profil kompanje?", | |||
| @@ -0,0 +1,7 @@ | |||
| export default (payment) => ({ | |||
| payerName: payment?.payerName || "", | |||
| companyName: payment?.companyName || "", | |||
| type: payment?.type || "", | |||
| date: payment?.date || "", | |||
| offerName: payment?.offerName || "", | |||
| }); | |||
| @@ -1,15 +1,121 @@ | |||
| import React from 'react' | |||
| import PropTypes from 'prop-types' | |||
| import { AdminPaymentPageContainer } from './AdminPaymentPage.styled' | |||
| import React, { useEffect, useMemo } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| AdminPaymentPageContainer, | |||
| AdminPaymentsHeader, | |||
| AdminPaymentsSearchField, | |||
| // FilterButtonIcon, | |||
| // FilterButtonContainer, | |||
| NewPaymentButton, | |||
| PaymentsList, | |||
| // FilterButton, | |||
| } from "./AdminPaymentPage.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import useSorting from "../../../hooks/useOffers/useSorting"; | |||
| import { sortPaymentsEnum } from "../../../enums/sortEnum"; | |||
| import CategoryCard from "../../../components/Cards/CategoryCard/CategoryCard"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { setManualSearchString } from "../../../store/actions/filters/filtersActions"; | |||
| import { toggleCreatePaymentModal } from "../../../store/actions/modal/modalActions"; | |||
| import { selectPayments } from "../../../store/selectors/paymentSelector"; | |||
| import { selectManualSearchString } from "../../../store/selectors/filtersSelectors"; | |||
| import { fetchPayments } from "../../../store/actions/payment/paymentActions"; | |||
| import { adminSortMethod } from "../../../util/helpers/adminSortHelper"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import { selectAllProfiles } from "../../../store/selectors/profileSelectors"; | |||
| import { fetchAllProfiles } from "../../../store/actions/profile/profileActions"; | |||
| const AdminPaymentPage = () => { | |||
| const { t } = useTranslation(); | |||
| const dispatch = useDispatch(); | |||
| const sorting = useSorting(() => {}, sortPaymentsEnum); | |||
| const payments = useSelector(selectPayments); | |||
| const users = useSelector(selectAllProfiles); | |||
| const manualSearchString = useSelector(selectManualSearchString); | |||
| const history = useHistory(); | |||
| const user = history?.location?.search?.split("=")[1]; | |||
| const userName = user?.replaceAll("+", " "); | |||
| const userId = users?.filter?.((user) => user.companyName === userName); | |||
| useEffect(() => { | |||
| dispatch(fetchPayments()); | |||
| dispatch(fetchAllProfiles()); | |||
| return () => { | |||
| dispatch(setManualSearchString("")); | |||
| sorting.clear(); | |||
| }; | |||
| }, []); | |||
| const paymentsToShow = useMemo(() => { | |||
| if (payments && history?.location?.search === "") { | |||
| return adminSortMethod(payments, manualSearchString, sorting); | |||
| } | |||
| if (history.location.search) { | |||
| const filteredPayments = payments?.filter( | |||
| (payment) => payment?.user?._id === userId[0]?._id | |||
| ); | |||
| return filteredPayments; | |||
| } | |||
| }, [ | |||
| payments, | |||
| manualSearchString, | |||
| sorting.selectedSortOptionLocally, | |||
| history.location.search, | |||
| ]); | |||
| const handleSearch = (value) => { | |||
| dispatch(setManualSearchString(value)); | |||
| }; | |||
| const showAddPaymentModal = () => { | |||
| dispatch(toggleCreatePaymentModal()); | |||
| }; | |||
| return ( | |||
| <AdminPaymentPageContainer>Admin payment</AdminPaymentPageContainer> | |||
| ) | |||
| } | |||
| <AdminPaymentPageContainer> | |||
| <AdminPaymentsSearchField | |||
| placeholder={t("admin.payment.placeholder")} | |||
| isAdmin | |||
| handleSearch={handleSearch} | |||
| /> | |||
| <NewPaymentButton | |||
| variant="contained" | |||
| buttoncolor={selectedTheme.colors.iconYellowColor} | |||
| textcolor={selectedTheme.colors.messageText} | |||
| onClick={showAddPaymentModal} | |||
| > | |||
| {t("admin.payment.addPayment")} | |||
| </NewPaymentButton> | |||
| {/* <FilterButtonContainer> | |||
| <FilterButton> | |||
| <FilterButtonIcon /> | |||
| </FilterButton> | |||
| </FilterButtonContainer> */} | |||
| <AdminPaymentsHeader | |||
| myOffers | |||
| payments | |||
| hideGrid | |||
| isAdmin | |||
| sorting={sorting} | |||
| hideBackButton | |||
| /> | |||
| <PaymentsList> | |||
| {paymentsToShow?.map((payment) => ( | |||
| <CategoryCard | |||
| key={payment._id} | |||
| category={payment} | |||
| type="payment" | |||
| secondLabel={t("admin.payment.date")} | |||
| dontNavigate | |||
| hideCheckButton | |||
| payments | |||
| /> | |||
| ))} | |||
| </PaymentsList> | |||
| </AdminPaymentPageContainer> | |||
| ); | |||
| }; | |||
| AdminPaymentPage.propTypes = { | |||
| children: PropTypes.node, | |||
| } | |||
| children: PropTypes.node, | |||
| }; | |||
| export default AdminPaymentPage | |||
| export default AdminPaymentPage; | |||
| @@ -1,6 +1,87 @@ | |||
| import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import SearchField from "../../../components/TextFields/SearchField/SearchField"; | |||
| import Header from "../../../components/MarketPlace/Header/Header"; | |||
| import { PrimaryButton } from "../../../components/Buttons/PrimaryButton/PrimaryButton"; | |||
| import { ReactComponent as Filter } from "../../../assets/images/svg/filter.svg"; | |||
| export const AdminPaymentPageContainer = styled(Box)` | |||
| ` | |||
| padding: 60px; | |||
| padding-top: 38px; | |||
| min-height: 100vh; | |||
| padding-bottom: 100px; | |||
| position: relative; | |||
| @media (max-width: 600px) { | |||
| padding: 18px; | |||
| min-height: calc(100vh - 72px); | |||
| padding-bottom: 100px; | |||
| top: 65px; | |||
| } | |||
| `; | |||
| export const AdminPaymentsSearchField = styled(SearchField)` | |||
| top: -15px; | |||
| @media (max-width: 900px) { | |||
| margin-top: 72px; | |||
| } | |||
| @media (max-width: 600px) { | |||
| display: none; | |||
| } | |||
| `; | |||
| export const AdminPaymentsHeader = styled(Header)` | |||
| /* top: 40px; */ | |||
| top: 0; | |||
| @media (min-width: 600px) and (max-width: 900px) { | |||
| & > div:nth-child(2) { | |||
| display: none; | |||
| } | |||
| } | |||
| @media (max-width: 600px) { | |||
| top: 0; | |||
| margin-top: 0px; | |||
| & > div { | |||
| margin-top: 0; | |||
| } | |||
| & > div > div > div { | |||
| top: 25px; | |||
| left: 0; | |||
| } | |||
| } | |||
| `; | |||
| export const PaymentsList = styled(Box)``; | |||
| export const NewPaymentButton = styled(PrimaryButton)` | |||
| position: relative; | |||
| margin-left: auto; | |||
| height: 48px; | |||
| width: 224px; | |||
| & button { | |||
| font-weight: 700; | |||
| } | |||
| @media (max-width: 600px) { | |||
| /* bottom: 18px; | |||
| right: 16px; */ | |||
| } | |||
| `; | |||
| export const FilterButtonContainer = styled(Box)` | |||
| display: flex; | |||
| justify-content: end; | |||
| margin-top: 20px; | |||
| `; | |||
| export const FilterButton = styled(Box)` | |||
| background-color: #e4e4e4; | |||
| border-radius: 100%; | |||
| width: 40px; | |||
| height: 40px; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| `; | |||
| export const FilterButtonIcon = styled(Filter)``; | |||
| @@ -219,4 +219,10 @@ export default { | |||
| getUserReviewsAsAdmin: "admin/reviews/{userId}", | |||
| }, | |||
| }, | |||
| payment: { | |||
| getUsersPayments: "admin/payments", | |||
| postUsersPayments: "admin/payments", | |||
| putUsersPayments: "admin/payments/{id}", | |||
| deleteUsersPayments: "admin/payments/{id}", | |||
| }, | |||
| }; | |||
| @@ -0,0 +1,28 @@ | |||
| import { | |||
| deleteRequest, | |||
| getRequest, | |||
| postRequest, | |||
| putRequest, | |||
| replaceInUrl, | |||
| } from "."; | |||
| import apiEndpoints from "./apiEndpoints"; | |||
| export const attemptFetchPayments = () => | |||
| getRequest(apiEndpoints.payment.getUsersPayments); | |||
| export const attemptAddNewPayment = (payload) => | |||
| postRequest(apiEndpoints.payment.postUsersPayments, payload); | |||
| export const attemptEditPayment = (payload) => | |||
| putRequest( | |||
| replaceInUrl(apiEndpoints.payment.putUsersPayments, { | |||
| id: payload.id, | |||
| }), | |||
| payload.body | |||
| ); | |||
| export const attemptDeletePayment = (payload) => | |||
| deleteRequest( | |||
| replaceInUrl(apiEndpoints.payment.deleteUsersPayments, { | |||
| id: payload.id, | |||
| }) | |||
| ); | |||
| @@ -6,6 +6,7 @@ export const CLEAR_FILTERS = createClearType(FILTERS_SCOPE); | |||
| export const SET_CATEGORY = createSetType("FILTERS_SET_CATEGORY"); | |||
| export const SET_SUBCATEGORY = createSetType("FILTERS_SET_SUBCATEGORY"); | |||
| export const SET_LOCATIONS = createSetType("FILTERS_SET_LOCATIONS"); | |||
| export const SET_PAYMENTS = createSetType("FILTERS_SET_PAYMENTS"); | |||
| export const SET_COMPANY = createSetType("FILTERS_SET_COMPANY"); | |||
| export const SET_SORT_OPTION = createSetType("FILTERS_SET_SORT_OPTION"); | |||
| export const SET_IS_APPLIED = createSetType("FILTERS_SET_IS_APPLIED"); | |||
| @@ -7,6 +7,7 @@ import { | |||
| SET_IS_APPLIED, | |||
| SET_LOCATIONS, | |||
| SET_MANUAL_SEARCH_STRING, | |||
| SET_PAYMENTS, | |||
| SET_QUERY_STRING, | |||
| SET_SEARCH_STRING, | |||
| SET_SORT_OPTION, | |||
| @@ -36,6 +37,10 @@ export const setFilteredLocations = (payload) => ({ | |||
| type: SET_LOCATIONS, | |||
| payload, | |||
| }); | |||
| export const setFilteredPayments = (payload) => ({ | |||
| type: SET_PAYMENTS, | |||
| payload, | |||
| }); | |||
| export const setFilteredCompany = (payload) => ({ | |||
| type: SET_COMPANY, | |||
| payload, | |||
| @@ -9,5 +9,8 @@ export const TOGGLE_DELETE_CATEGORY = createSetType("TOGGLE_DELETE_CATEGORY"); | |||
| export const TOGGLE_CREATE_REVIEW = createSetType("TOGGLE_CREATE_REVIEW"); | |||
| export const TOGGLE_DELETE_REVIEW = createSetType("TOGGLE_DELETE_REVIEW"); | |||
| export const TOGGLE_EDIT_PROFILE = createSetType("TOGGLE_EDIT_PROFILE"); | |||
| export const TOGGLE_CREATE_PAYMENT = createSetType("TOGGLE_CREATE_PAYMENT"); | |||
| export const TOGGLE_EDIT_PAYMENT = createSetType("TOGGLE_EDIT_PAYMENT"); | |||
| export const TOGGLE_DELETE_PAYMENT = createSetType("TOGGLE_DELETE_PAYMENT"); | |||
| export const SET_MODAL_TYPE = createSetType("SET_MODAL_TYPE"); | |||
| export const CLOSE_MODAL = createClearType("CLOSE_MODAL"); | |||
| @@ -2,12 +2,15 @@ import { | |||
| CLOSE_MODAL, | |||
| TOGGLE_CREATE_CATEGORY, | |||
| TOGGLE_CREATE_OFFER, | |||
| TOGGLE_CREATE_PAYMENT, | |||
| TOGGLE_CREATE_REVIEW, | |||
| TOGGLE_DELETE_CATEGORY, | |||
| TOGGLE_DELETE_OFFER, | |||
| TOGGLE_DELETE_PAYMENT, | |||
| TOGGLE_DELETE_REVIEW, | |||
| TOGGLE_EDIT_CATEOGRY, | |||
| TOGGLE_EDIT_OFFER, | |||
| TOGGLE_EDIT_PAYMENT, | |||
| TOGGLE_EDIT_PROFILE, | |||
| } from "./modalActionConstants"; | |||
| @@ -47,6 +50,18 @@ export const toggleEditProfileModal = (payload) => ({ | |||
| type: TOGGLE_EDIT_PROFILE, | |||
| payload, | |||
| }); | |||
| export const toggleCreatePaymentModal = (payload) => ({ | |||
| type: TOGGLE_CREATE_PAYMENT, | |||
| payload, | |||
| }); | |||
| export const toggleEditPaymentModal = (payload) => ({ | |||
| type: TOGGLE_EDIT_PAYMENT, | |||
| payload, | |||
| }); | |||
| export const toggleDeletePaymentModal = (payload) => ({ | |||
| type: TOGGLE_DELETE_PAYMENT, | |||
| payload, | |||
| }); | |||
| export const closeModal = () => ({ | |||
| type: CLOSE_MODAL, | |||
| }); | |||
| @@ -0,0 +1,28 @@ | |||
| import { | |||
| createErrorType, | |||
| createFetchType, | |||
| createSetType, | |||
| createSuccessType, | |||
| } from "../actionHelpers"; | |||
| const PAYMENTS_SCOPE = "PAYMENTS_SCOPE"; | |||
| export const PAYMENTS_FETCH = createFetchType(PAYMENTS_SCOPE); | |||
| export const PAYMENTS_FETCH_SUCCESS = createSuccessType(PAYMENTS_SCOPE); | |||
| export const PAYMENTS_FETCH_ERROR = createErrorType(PAYMENTS_SCOPE); | |||
| export const PAYMENTS_ADD_SCOPE = "PAYMENTS_ADD_SCOPE"; | |||
| export const PAYMENTS_ADD = createFetchType(PAYMENTS_ADD_SCOPE); | |||
| export const PAYMENTS_ADD_SUCCESS = createSuccessType(PAYMENTS_ADD_SCOPE); | |||
| export const PAYMENTS_ADD_ERROR = createErrorType(PAYMENTS_ADD_SCOPE); | |||
| export const PAYMENTS_EDIT_SCOPE = "PAYMENTS_EDIT_SCOPE"; | |||
| export const PAYMENTS_EDIT = createFetchType(PAYMENTS_EDIT_SCOPE); | |||
| export const PAYMENTS_EDIT_SUCCESS = createSuccessType(PAYMENTS_EDIT_SCOPE); | |||
| export const PAYMENTS_EDIT_ERROR = createErrorType(PAYMENTS_EDIT_SCOPE); | |||
| export const PAYMENTS_DELETE_SCOPE = "PAYMENTS_DELETE_SCOPE"; | |||
| export const PAYMENTS_DELETE = createFetchType(PAYMENTS_DELETE_SCOPE); | |||
| export const PAYMENTS_DELETE_SUCCESS = createSuccessType(PAYMENTS_DELETE_SCOPE); | |||
| export const PAYMENTS_DELETE_ERROR = createErrorType(PAYMENTS_DELETE_SCOPE); | |||
| export const PAYMENTS_SET = createSetType("PAYMENTS_SET"); | |||
| @@ -0,0 +1,60 @@ | |||
| import { | |||
| PAYMENTS_ADD, | |||
| PAYMENTS_ADD_ERROR, | |||
| PAYMENTS_ADD_SUCCESS, | |||
| PAYMENTS_DELETE, | |||
| PAYMENTS_DELETE_ERROR, | |||
| PAYMENTS_DELETE_SUCCESS, | |||
| PAYMENTS_EDIT, | |||
| PAYMENTS_EDIT_ERROR, | |||
| PAYMENTS_EDIT_SUCCESS, | |||
| PAYMENTS_FETCH, | |||
| PAYMENTS_FETCH_ERROR, | |||
| PAYMENTS_FETCH_SUCCESS, | |||
| PAYMENTS_SET, | |||
| } from "./paymentActionConstants"; | |||
| export const fetchPayments = () => ({ | |||
| type: PAYMENTS_FETCH, | |||
| }); | |||
| export const setPayments = (payload) => ({ | |||
| type: PAYMENTS_SET, | |||
| payload, | |||
| }); | |||
| export const fetchPaymentsSuccess = () => ({ | |||
| type: PAYMENTS_FETCH_SUCCESS, | |||
| }); | |||
| export const fetchPaymentsError = () => ({ | |||
| type: PAYMENTS_FETCH_ERROR, | |||
| }); | |||
| export const addPayment = (payload) => ({ | |||
| type: PAYMENTS_ADD, | |||
| payload, | |||
| }); | |||
| export const addPaymentSuccess = () => ({ | |||
| type: PAYMENTS_ADD_SUCCESS, | |||
| }); | |||
| export const addPaymentError = () => ({ | |||
| type: PAYMENTS_ADD_ERROR, | |||
| }); | |||
| export const editPayment = (payload) => ({ | |||
| type: PAYMENTS_EDIT, | |||
| payload, | |||
| }); | |||
| export const editPaymentSuccess = () => ({ | |||
| type: PAYMENTS_EDIT_SUCCESS, | |||
| }); | |||
| export const editPaymentError = () => ({ | |||
| type: PAYMENTS_EDIT_ERROR, | |||
| }); | |||
| export const deletePayment = (payload) => ({ | |||
| type: PAYMENTS_DELETE, | |||
| payload, | |||
| }); | |||
| export const deletePaymentSuccess = () => ({ | |||
| type: PAYMENTS_DELETE_SUCCESS, | |||
| }); | |||
| export const deletePaymentError = () => ({ | |||
| type: PAYMENTS_DELETE_ERROR, | |||
| }); | |||
| @@ -7,6 +7,7 @@ import { | |||
| SET_IS_APPLIED, | |||
| SET_LOCATIONS, | |||
| SET_MANUAL_SEARCH_STRING, | |||
| SET_PAYMENTS, | |||
| SET_QUERY_STRING, | |||
| SET_SEARCH_STRING, | |||
| SET_SORT_OPTION, | |||
| @@ -19,6 +20,7 @@ const initialState = { | |||
| category: null, | |||
| subcategory: null, | |||
| locations: [], | |||
| payments: [], | |||
| company: [], | |||
| sortOption: null, | |||
| isApplied: false, | |||
| @@ -27,6 +29,7 @@ const initialState = { | |||
| categoryString: "", | |||
| subcategoryString: "", | |||
| locationsString: "", | |||
| paymentsString: "", | |||
| companyString: "", | |||
| text: "", | |||
| }, | |||
| @@ -42,6 +45,7 @@ export default createReducer( | |||
| [SET_CATEGORY]: setFilteredCategory, | |||
| [SET_SUBCATEGORY]: setFilteredSubcategory, | |||
| [SET_LOCATIONS]: setFilteredLocations, | |||
| [SET_PAYMENTS]: setFilteredPayments, | |||
| [SET_COMPANY]: setFilteredCompany, | |||
| [SET_SORT_OPTION]: setFilteredSortOption, | |||
| [SET_IS_APPLIED]: setIsAppliedStatus, | |||
| @@ -103,6 +107,16 @@ function setFilteredLocations(state, { payload }) { | |||
| }, | |||
| }; | |||
| } | |||
| function setFilteredPayments(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| filters: { | |||
| ...state.filters, | |||
| payments: payload, | |||
| }, | |||
| }; | |||
| } | |||
| function setFilteredCompany(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| @@ -16,6 +16,7 @@ import exchangeReducer from "./exchange/exchangeReducer"; | |||
| import reviewReducer from "./review/reviewReducer"; | |||
| import appReducer from "./app/appReducer"; | |||
| import modalReducer from "./modal/modalReducer"; | |||
| import paymentReducer from "./payment/paymentReducer"; | |||
| const loginPersistConfig = { | |||
| key: "login", | |||
| @@ -40,6 +41,11 @@ const locationsPersistConfig = { | |||
| storage: storage, | |||
| transform: [createFilter("locations", ["_id", "city"])], | |||
| }; | |||
| const paymentsPersistConfig = { | |||
| key: "payment", | |||
| storage: storage, | |||
| transform: [createFilter("payment", ["_id", "payerName"])], | |||
| }; | |||
| const profilePersistConfig = { | |||
| key: "profile", | |||
| storage: storage, | |||
| @@ -48,8 +54,8 @@ const profilePersistConfig = { | |||
| const chatPersistConfig = { | |||
| key: "chat", | |||
| storage: storage, | |||
| transform: [createFilter("chat", ["latestChats"])] | |||
| } | |||
| transform: [createFilter("chat", ["latestChats"])], | |||
| }; | |||
| export default combineReducers({ | |||
| login: persistReducer(loginPersistConfig, loginReducer), | |||
| @@ -59,6 +65,7 @@ export default combineReducers({ | |||
| offers: offersReducer, | |||
| categories: persistReducer(categoriesPersistConfig, categoriesReducer), | |||
| locations: persistReducer(locationsPersistConfig, locationsReducer), | |||
| payment: persistReducer(paymentsPersistConfig, paymentReducer), | |||
| profile: persistReducer(profilePersistConfig, profileReducer), | |||
| chat: persistReducer(chatPersistConfig, chatReducer), | |||
| queryString: queryStringReducer, | |||
| @@ -1,5 +1,5 @@ | |||
| import { | |||
| CLOSE_MODAL, | |||
| CLOSE_MODAL, | |||
| SET_MODAL_TYPE, | |||
| TOGGLE_CREATE_CATEGORY, | |||
| TOGGLE_CREATE_OFFER, | |||
| @@ -10,6 +10,8 @@ import { | |||
| TOGGLE_EDIT_CATEOGRY, | |||
| TOGGLE_EDIT_OFFER, | |||
| TOGGLE_EDIT_PROFILE, | |||
| TOGGLE_CREATE_PAYMENT, | |||
| TOGGLE_EDIT_PAYMENT, | |||
| } from "../../actions/modal/modalActionConstants"; | |||
| import createReducer from "../../utils/createReducer"; | |||
| @@ -23,6 +25,8 @@ const initialState = { | |||
| createReviewModal: false, | |||
| editProfile: false, | |||
| toggleDeleteReview: false, | |||
| createPaymentModal: false, | |||
| editPaymentModal: false, | |||
| props: {}, | |||
| }; | |||
| @@ -37,6 +41,8 @@ export default createReducer( | |||
| [TOGGLE_CREATE_REVIEW]: toggleCreateReview, | |||
| [TOGGLE_DELETE_REVIEW]: toggleDeleteReview, | |||
| [TOGGLE_EDIT_PROFILE]: toggleEditProfile, | |||
| [TOGGLE_CREATE_PAYMENT]: toggleCreatePayment, | |||
| [TOGGLE_EDIT_PAYMENT]: toggleEditPayment, | |||
| [SET_MODAL_TYPE]: setModalType, | |||
| [CLOSE_MODAL]: closeModal, | |||
| }, | |||
| @@ -105,6 +111,20 @@ function toggleEditProfile(state, { payload }) { | |||
| props: payload, | |||
| }; | |||
| } | |||
| function toggleCreatePayment(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| createPaymentModal: !state.createPaymentModal, | |||
| props: payload, | |||
| }; | |||
| } | |||
| function toggleEditPayment(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| editPaymentModal: !state.editPaymentModal, | |||
| props: payload, | |||
| }; | |||
| } | |||
| function setModalType(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| @@ -112,5 +132,5 @@ function setModalType(state, { payload }) { | |||
| }; | |||
| } | |||
| function closeModal() { | |||
| return initialState; | |||
| return initialState; | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| import { PAYMENTS_SET } from "../../actions/payment/paymentActionConstants"; | |||
| import createReducer from "../../utils/createReducer"; | |||
| const initialState = { | |||
| payments: [], | |||
| }; | |||
| export default createReducer( | |||
| { | |||
| [PAYMENTS_SET]: setPayments, | |||
| }, | |||
| initialState | |||
| ); | |||
| function setPayments(state, action) { | |||
| return { | |||
| ...state, | |||
| payments: action.payload, | |||
| }; | |||
| } | |||
| @@ -1,17 +1,18 @@ | |||
| import { all } from 'redux-saga/effects'; | |||
| import adminSaga from './adminSaga'; | |||
| import categoriesSaga from './categoriesSaga'; | |||
| import chatSaga from './chatSaga'; | |||
| import counterSaga from './counterSaga'; | |||
| import exchangeSaga from './exchangeSaga'; | |||
| import forgotPasswordSaga from './forgotPasswordSaga'; | |||
| import locationsSaga from './locationsSaga'; | |||
| import loginSaga from './loginSaga'; | |||
| import offersSaga from './offersSaga'; | |||
| import profileSaga from './profileSaga'; | |||
| import queryStringSaga from './queryStringSaga'; | |||
| import registerSaga from './registerSaga'; | |||
| import reviewSaga from './reviewSaga'; | |||
| import { all } from "redux-saga/effects"; | |||
| import adminSaga from "./adminSaga"; | |||
| import categoriesSaga from "./categoriesSaga"; | |||
| import chatSaga from "./chatSaga"; | |||
| import counterSaga from "./counterSaga"; | |||
| import exchangeSaga from "./exchangeSaga"; | |||
| import forgotPasswordSaga from "./forgotPasswordSaga"; | |||
| import locationsSaga from "./locationsSaga"; | |||
| import loginSaga from "./loginSaga"; | |||
| import offersSaga from "./offersSaga"; | |||
| import profileSaga from "./profileSaga"; | |||
| import queryStringSaga from "./queryStringSaga"; | |||
| import registerSaga from "./registerSaga"; | |||
| import reviewSaga from "./reviewSaga"; | |||
| import paymentSaga from "./paymentSaga"; | |||
| export default function* rootSaga() { | |||
| yield all([ | |||
| @@ -27,6 +28,7 @@ export default function* rootSaga() { | |||
| exchangeSaga(), | |||
| reviewSaga(), | |||
| counterSaga(), | |||
| adminSaga() | |||
| adminSaga(), | |||
| paymentSaga(), | |||
| ]); | |||
| } | |||
| @@ -0,0 +1,82 @@ | |||
| import { all, call, put, takeLatest } from "@redux-saga/core/effects"; | |||
| import { | |||
| PAYMENTS_ADD, | |||
| PAYMENTS_DELETE, | |||
| PAYMENTS_EDIT, | |||
| PAYMENTS_FETCH, | |||
| } from "../actions/payment/paymentActionConstants"; | |||
| import { | |||
| fetchPaymentsError, | |||
| fetchPaymentsSuccess, | |||
| setPayments, | |||
| } from "../actions/payment/paymentActions"; | |||
| import { | |||
| attemptAddNewPayment, | |||
| attemptDeletePayment, | |||
| attemptEditPayment, | |||
| attemptFetchPayments, | |||
| } from "../../request/paymentsRequest"; | |||
| import { fetchPayments } from "../actions/payment/paymentActions"; | |||
| function* fetchAllPayments() { | |||
| try { | |||
| const data = yield call(attemptFetchPayments); | |||
| if (!data?.data) throw new Error(); | |||
| yield put(setPayments(data.data)); | |||
| yield put(fetchPaymentsSuccess()); | |||
| } catch (e) { | |||
| yield put(fetchPaymentsError()); | |||
| } | |||
| } | |||
| function* createPayment(payload) { | |||
| try { | |||
| yield call(attemptAddNewPayment, payload.payload); | |||
| yield put(fetchPayments()); | |||
| if (payload.payload.handleApiResponseSuccess) { | |||
| yield call(payload.payload.handleApiResponseSuccess); | |||
| } | |||
| } catch (e) { | |||
| console.dir(e); | |||
| } | |||
| } | |||
| function* editPayment(payload) { | |||
| try { | |||
| yield call(attemptEditPayment, { | |||
| id: payload.payload.id, | |||
| body: { | |||
| type: payload.payload.type, | |||
| payerName: payload.payload.payerName, | |||
| userId: payload.payload.userId, | |||
| offerId: payload.payload.offerId, | |||
| date: payload.payload.date, | |||
| }, | |||
| }); | |||
| if (payload.payload.handleApiResponseSuccess) { | |||
| yield call(payload.payload.handleApiResponseSuccess); | |||
| } | |||
| } catch (e) { | |||
| console.dir(e); | |||
| } | |||
| } | |||
| function* deletePayment(payload) { | |||
| try { | |||
| yield call(attemptDeletePayment, { id: payload.payload.id }); | |||
| if (payload.payload.handleApiDeletePaymentResponseSuccess) { | |||
| yield call(payload.payload.handleApiDeletePaymentResponseSuccess); | |||
| } | |||
| } catch (e) { | |||
| console.dir(e); | |||
| } | |||
| } | |||
| export default function* paymentSaga() { | |||
| yield all([ | |||
| takeLatest(PAYMENTS_FETCH, fetchAllPayments), | |||
| takeLatest(PAYMENTS_ADD, createPayment), | |||
| takeLatest(PAYMENTS_EDIT, editPayment), | |||
| takeLatest(PAYMENTS_DELETE, deletePayment), | |||
| ]); | |||
| } | |||
| @@ -97,6 +97,7 @@ function* fetchAllProfilesAsAdmin({ payload }) { | |||
| } | |||
| const data = yield call(attemptFetchAllProfilesAsAdmin, queryString); | |||
| console.log(data); | |||
| if (data) yield put(setAllProfiles(data.data)); | |||
| yield put(fetchAllProfilesAsAdminSuccess()); | |||
| } catch (e) { | |||
| @@ -18,6 +18,10 @@ export const selectSelectedLocations = createSelector( | |||
| filtersSelector, | |||
| (state) => state.filters.locations | |||
| ); | |||
| export const selectSelectedPayments = createSelector( | |||
| filtersSelector, | |||
| (state) => state.filters.payments | |||
| ); | |||
| export const selectSelectedCompany = createSelector( | |||
| filtersSelector, | |||
| (state) => state.filters.company | |||
| @@ -0,0 +1,8 @@ | |||
| import { createSelector } from "reselect"; | |||
| const paymentSelector = (state) => state.payment; | |||
| export const selectPayments = createSelector( | |||
| paymentSelector, | |||
| (state) => state.payments | |||
| ); | |||
| @@ -1,4 +1,5 @@ | |||
| import { sortCategoriesEnum } from "../../enums/sortEnum"; | |||
| import { sortPaymentsEnum } from "../../enums/sortEnum"; | |||
| export const adminSortMethod = (arrayOfItems, manualSearchString, sorting) => { | |||
| let arrayOfItemsToReturn = [...arrayOfItems]; | |||
| @@ -6,7 +7,10 @@ export const adminSortMethod = (arrayOfItems, manualSearchString, sorting) => { | |||
| arrayOfItemsToReturn = arrayOfItems.filter( | |||
| (item) => | |||
| item?.city?.toLowerCase()?.includes(manualSearchString.toLowerCase()) || | |||
| item?.name?.toLowerCase()?.includes(manualSearchString.toLowerCase()) | |||
| item?.name?.toLowerCase()?.includes(manualSearchString.toLowerCase()) || | |||
| item?.payerName | |||
| ?.toLowerCase() | |||
| ?.includes(manualSearchString.toLowerCase()) | |||
| ); | |||
| if (sorting?.selectedSortOptionLocally !== sortCategoriesEnum.INITIAL) { | |||
| if (sorting?.selectedSortOptionLocally === sortCategoriesEnum.POPULAR) { | |||
| @@ -35,5 +39,29 @@ export const adminSortMethod = (arrayOfItems, manualSearchString, sorting) => { | |||
| }); | |||
| } | |||
| } | |||
| // if ("payerName" in arrayOfItems[0]) { | |||
| if (sorting?.selectedSortOptionLocally === sortPaymentsEnum.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() | |||
| ); | |||
| return firstCreated - secondCreated; | |||
| }); | |||
| } | |||
| if (sorting?.selectedSortOptionLocally === sortPaymentsEnum.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; | |||
| }; | |||
| @@ -0,0 +1,16 @@ | |||
| import * as Yup from "yup"; | |||
| import i18n from "../i18n"; | |||
| export default () => | |||
| Yup.object().shape({ | |||
| payerName: Yup.string().required( | |||
| i18n.t("admin.payment.errors.nameRequired") | |||
| ), | |||
| companyName: Yup.string().required( | |||
| i18n.t("admin.payment.errors.companyRequired") | |||
| ), | |||
| offerName: Yup.string().required( | |||
| i18n.t("admin.payment.errors.offerRequired") | |||
| ), | |||
| type: Yup.string().required(i18n.t("admin.payment.errors.typeRequired")), | |||
| date: Yup.string().required(i18n.t("admin.payment.errors.dateRequired")), | |||
| }); | |||