Przeglądaj źródła

Partly finished feature 649

feature/649
djordjemitrovic00 3 lat temu
rodzic
commit
d771aa9d9b
30 zmienionych plików z 713 dodań i 84 usunięć
  1. 11
    0
      src/assets/images/svg/block.svg
  2. 75
    0
      src/components/Cards/ProfileCard/BigProfileCard/BigProfileCard.js
  3. 313
    0
      src/components/Cards/ProfileCard/BigProfileCard/BigProfileCard.styled.js
  4. 19
    2
      src/components/Cards/ProfileCard/EditProfile/EditProfile.js
  5. 2
    0
      src/components/Cards/ProfileCard/ProfileContact/ProfileContact.js
  6. 1
    1
      src/components/Cards/ProfileCard/ProfileContact/ProfileContact.styled.js
  7. 7
    1
      src/components/Cards/ProfileCard/ProfileMainInfo/ProfileMainInfo.js
  8. 5
    4
      src/components/Cards/ProfileCard/ProfileMainInfo/ProfileMainInfo.styled.js
  9. 2
    2
      src/components/MarketPlace/Header/Header.js
  10. 10
    1
      src/components/MarketPlace/Header/Header.styled.js
  11. 3
    0
      src/components/MarketPlace/MarketPlace.js
  12. 6
    1
      src/components/MarketPlace/Offers/HeaderMyOffers.js/HeadersMyOffers.js
  13. 21
    12
      src/components/MarketPlace/Offers/Offers.js
  14. 4
    21
      src/components/Popovers/MyProfile/MyProfile.js
  15. 5
    1
      src/components/Popovers/MyProfile/MyProfile.styled.js
  16. 45
    0
      src/components/Popovers/MyProfile/PricesButton/PricesButton.js
  17. 8
    0
      src/components/Popovers/MyProfile/PricesButton/PricesButton.styled.js
  18. 6
    4
      src/i18n/resources/rs.js
  19. 18
    2
      src/pages/AdminUsersPage/AdminUsersPage.js
  20. 2
    1
      src/request/apiEndpoints.js
  21. 19
    6
      src/request/offersRequest.js
  22. 3
    0
      src/request/profileRequest.js
  23. 5
    0
      src/store/actions/offers/offersActionConstants.js
  24. 15
    0
      src/store/actions/offers/offersActions.js
  25. 6
    0
      src/store/actions/profile/profileActionConstants.js
  26. 24
    3
      src/store/actions/profile/profileActions.js
  27. 16
    3
      src/store/reducers/profile/profileReducer.js
  28. 18
    0
      src/store/saga/offersSaga.js
  29. 27
    6
      src/store/saga/profileSaga.js
  30. 17
    13
      src/store/selectors/profileSelectors.js

+ 11
- 0
src/assets/images/svg/block.svg Wyświetl plik

@@ -0,0 +1,11 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1412_9649)">
<path d="M9 16.5C13.1421 16.5 16.5 13.1421 16.5 9C16.5 4.85786 13.1421 1.5 9 1.5C4.85786 1.5 1.5 4.85786 1.5 9C1.5 13.1421 4.85786 16.5 9 16.5Z" stroke="#5A3984" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3.69727 3.69727L14.3023 14.3023" stroke="#5A3984" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_1412_9649">
<rect width="18" height="18" fill="white"/>
</clipPath>
</defs>
</svg>

+ 75
- 0
src/components/Cards/ProfileCard/BigProfileCard/BigProfileCard.js Wyświetl plik

@@ -0,0 +1,75 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import {
BlockIcon,
BlockIconContainer,
CheckButton,
EditButton,
EditIcon,
ProfileCardContainer,
ProfileCardWrapper,
ProfileInfoContainer,
RemoveIcon,
RemoveIconContainer,
} from "./BigProfileCard.styled";
import ProfileMainInfo from "../ProfileMainInfo/ProfileMainInfo";
import ProfileContact from "../ProfileContact/ProfileContact";
import EditProfile from "../EditProfile/EditProfile";
import selectedTheme from "../../../../themes";
import { useTranslation } from "react-i18next";

const BigProfileCard = (props) => {
const { t } = useTranslation();
const [editProfileModal, setEditProfileModal] = useState(false);
const closeModalHandler = () => {
setEditProfileModal(false);
};
const removeUser = () => {};
const blockUser = () => {};
return (
<>
<ProfileCardContainer>
<ProfileCardWrapper variant="outlined">
<EditButton onClick={() => setEditProfileModal(true)}>
<EditIcon />
</EditButton>
<RemoveIconContainer onClick={removeUser}>
<RemoveIcon />
</RemoveIconContainer>
<BlockIconContainer onClick={blockUser}>
<BlockIcon />
</BlockIconContainer>
<ProfileInfoContainer>
{/* Profile Main Info */}
<ProfileMainInfo profile={props.profile} isAdmin />
{/* Profile Contact */}
<ProfileContact profile={props.profile} isAdmin />
</ProfileInfoContainer>
<CheckButton
variant={"outlined"}
buttoncolor={selectedTheme.colors.primaryPurple}
textcolor={selectedTheme.colors.primaryPurple}
style={{ fontWeight: "600" }}
>
{t("admin.users.checkProfile")}
</CheckButton>
</ProfileCardWrapper>
</ProfileCardContainer>
{editProfileModal && (
<EditProfile
profile={props.profile}
closeModalHandler={closeModalHandler}
reFetchProfile={() => {}}
isAdmin
userId={props.profile._id}
/>
)}
</>
);
};

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

export default BigProfileCard;

+ 313
- 0
src/components/Cards/ProfileCard/BigProfileCard/BigProfileCard.styled.js Wyświetl plik

@@ -0,0 +1,313 @@
import styled from "styled-components";
import { Card, Typography, Grid, Box } from "@mui/material";
import selectedTheme from "../../../../themes";
import { ReactComponent as Edit } from "../../../../assets/images/svg/edit.svg";
// import { ReactComponent as Pocket } from "../../../assets/images/svg/pocket.svg";
// import { ReactComponent as Globe } from "../../../assets/images/svg/globe.svg";
import { ReactComponent as Mail } from "../../../../assets/images/svg/mail.svg";
import { ReactComponent as Remove } from "../../../../assets/images/svg/trash.svg";
import { ReactComponent as Block } from "../../../../assets/images/svg/block.svg";
import { IconButton } from "../../../Buttons/IconButton/IconButton";
import { PrimaryButton } from "../../../Buttons/PrimaryButton/PrimaryButton";
// import { ReactComponent as Location } from "../../../assets/images/svg/location.svg";

export const ProfileCardContainer = styled(Box)`
width: 100%;
box-sizing: border-box;
max-height: 184px;
margin-top: 34px;
overflow: hidden;

@media (max-width: 1200px) {
padding: 0;
}
`;
export const EditIcon = styled(Edit)`
width: 18px;
height: 18px;
& path {
stroke: ${selectedTheme.colors.primaryPurple};
}
`;


export const MessageButton = styled(IconButton)`
width: 40px;
height: 40px;
position: absolute;
top: 18px;
right: 18px;
background-color: ${selectedTheme.colors.primaryIconBackgroundColor};
border-radius: 100%;
padding-top: 2px;
text-align: center;
@media (max-width: 600px) {
width: 30px;
height: 30px;
top: 16px;
right: 16px;
padding: 0;
${(props) =>
props.vertical &&
`
display: none;
`}
& button svg {
width: 16px;
height: 16px;
position: relative;
top: -3px;
left: -3.5px;
}
}
`;
export const EditButton = styled(MessageButton)`
right: 76px;
`;
export const ProfileCardWrapper = styled(Card)`
border: 1px solid ${selectedTheme.colors.borderNormal};
background: ${(props) =>
props.isMyProfile ? selectedTheme.colors.primaryPurple : "white"};
width: 100%;
min-width: fit-content;
padding: 1rem;
position: relative;
border-radius: 0 0 4px 4px;
`;
export const RemoveIconContainer = styled(MessageButton)`
display: block;
top: 18px;
right: 18px;
`;
export const RemoveIcon = styled(Remove)``;
export const BlockIconContainer = styled(MessageButton)`
display: block;
top: 18px;
right: 134px;
`;
export const BlockIcon = styled(Block)``;
export const CheckButton = styled(PrimaryButton)`
width: 180px;
height: 48px;
position: absolute;
bottom: 25px;
right: 9px;
& button:hover {
background-color: ${selectedTheme.colors.primaryPurple} !important;
color: white !important;
}
@media (max-width: 850px) {
display: none;
}
`;

// export const ProfileName = styled(Typography)`
// color: ${(props) =>
// props.isMyProfile
// ? selectedTheme.colors.primaryYellow
// : selectedTheme.colors.primaryPurple};
// font-weight: 700;
// font-size: 24px;
// font-family: ${selectedTheme.fonts.textFont};
// margin-bottom: 5px;
// @media (max-width: 600px) {
// font-size: 18px;
// }
// `;

// export const ProfilePIB = styled(Typography)`
// color: ${(props) =>
// props.isMyProfile ? "white" : selectedTheme.colors.primaryDarkText};
// margin-top: 0.18rem;
// font-family: ${selectedTheme.fonts.textFont};
// font-size: 16px;
// padding-top: 1px;
// @media (max-width: 600px) {
// font-size: 14px;
// }
// `;
// export const ProfilePIBContainer = styled(Grid)`
// display: flex;
// justify-content: center;
// align-items: center;
// position: relative;
// left: 5px;
// `;

// export const ProfileMainInfo = styled(Grid)`
// display: flex;
// justify-content: start;
// align-items: start;
// `;

// export const AvatarImageContainer = styled(Grid)`
// display: flex;
// justify-content: start;
// align-items: center;
// `;

// export const ProfileMainInfoGrid = styled(Grid)`
// display: flex;
// flex-direction: column;
// align-items: start;
// margin-left: 16px;
// `;

// export const ProfileContact = styled(Grid)`
// padding-top: 2rem;
// padding-bottom: 2rem;
// @media (max-width: 600px) {
// padding-bottom: 1rem;
// }
// `;

// export const ContactItem = styled(Typography)`
// margin-right: 2rem;
// margin-left: 0.4rem;
// color: ${(props) =>
// props.isMyProfile ? "white" : selectedTheme.colors.primaryDarkText};
// display: unset;
// font-family: ${selectedTheme.fonts.textFont};
// letter-spacing: 0.02em;
// font-size: 16px;
// position: relative;
// bottom: 1px;
// @media (max-width: 600px) {
// font-size: 14px;
// bottom: 4px;
// }
// `;

// export const StatsItem = styled(Typography)`
// margin-right: 2rem;
// display: unset;
// margin-left: 1rem;
// font-family: ${selectedTheme.fonts.textFont};
// font-size: 16px;
// margin-bottom: 2px;
// @media (max-width: 600px) {
// font-size: 12px;
// }
// `;

// export const ProfileStats = styled(Grid)`
// display: flex;
// justify-content: start;
// align-items: center;
// background: ${selectedTheme.colors.primaryDarkTextSecond};
// width: calc(100% + 2rem);
// padding-top: 1.3rem;
// padding-bottom: 1.3rem;
// margin-bottom: -1rem;
// margin-left: -1rem;
// border-radius: 0 0 4px 4px;
// `;
// export const AvatarImage = styled.img`
// min-height: 144px;
// min-width: 144px;
// width: 144px;
// height: 144px;
// border-radius: 100%;
// @media (max-width: 600px) {
// min-height: 90px;
// min-width: 90px;
// width: 90px;
// height: 90px;
// }
// `;

export const ProfileCardHeader = styled(Grid)`
display: flex;
justify-content: start;
align-items: center;
margin-bottom: 11px;
`;

export const HeaderTitle = styled(Typography)`
font-size: 16px;
font-family: ${selectedTheme.fonts.textFont};
color: ${selectedTheme.colors.primaryText};
position: relative;
@media (max-width: 600px) {
font-size: 12px;
}
`;
// export const PocketIcon = styled(Pocket)`
// width: 22px;
// height: 22px;
// position: relative;
// left: -5px;
// top: 2px;
// & path {
// stroke: #b4b4b4;
// }
// @media (max-width: 600px) {
// width: 14px;
// height: 14px;
// }
// `;
// export const MailIcon = styled(Mail)`
// height: 24px;
// width: 24px;
// & path {
// stroke: ${(props) =>
// props.isMyProfile
// ? selectedTheme.colors.iconMineProfileColor
// : selectedTheme.colors.iconProfileColor};
// }
// @media (max-width: 600px) {
// width: 14px;
// height: 14px;
// }
// `;
// export const GlobeIcon = styled(Globe)`
// height: 22px;
// width: 22px;
// & path {
// stroke: ${(props) =>
// props.isMyProfile
// ? selectedTheme.colors.iconMineProfileColor
// : selectedTheme.colors.iconProfileColor};
// }
// @media (max-width: 600px) {
// width: 14px;
// height: 14px;
// }
// `;
// export const LocationIcon = styled(Location)`
// height: 22px;
// width: 22px;
// & path {
// stroke: ${(props) =>
// props.isMyProfile
// ? selectedTheme.colors.iconMineProfileColor
// : selectedTheme.colors.iconProfileColor};
// }
// @media (max-width: 600px) {
// width: 14px;
// height: 14px;
// }
// `;
export const MessageIcon = styled(Mail)`
width: 19.5px;
height: 19.5px;
position: relative;
left: 1px;
top: 3px;
& path {
stroke: ${selectedTheme.colors.primaryYellow};
}
@media (max-width: 600px) {
width: 16px;
height: 16px;
left: -1px;
top: 1px;
}
`;

export const ProfileInfoContainer = styled(Grid)`
display: flex;
flex-direction: column;
justify-content: center;
align-items: start;
`;

+ 19
- 2
src/components/Cards/ProfileCard/EditProfile/EditProfile.js Wyświetl plik

@@ -19,7 +19,7 @@ import { ReactComponent as ArrowBack } from "../../../../assets/images/svg/arrow
import { ReactComponent as CloseIcon } from "../../../../assets/images/svg/close-modal.svg";
import { useTranslation } from "react-i18next";
import {
editMineProfile,
editProfile,
fetchMineProfile,
} from "../../../../store/actions/profile/profileActions";
import { useDispatch, useSelector } from "react-redux";
@@ -56,7 +56,22 @@ const EditProfile = (props) => {
};

const handleSubmit = (values) => {
dispatch(editMineProfile({ ...values, handleApiResponseSuccess }));
if (props.isAdmin) {
dispatch(
editProfile({
userId: props.userId,
...values,
handleApiResponseSuccess,
})
);
} else {
dispatch(
editProfile({
...values,
handleApiResponseSuccess,
})
);
}
props.closeModalHandler();
};
const initialValues = useMemo(
@@ -166,6 +181,8 @@ EditProfile.propTypes = {
closeModalHandler: PropTypes.func,
setImage: PropTypes.func,
reFetchProfile: PropTypes.func,
isAdmin: PropTypes.bool,
userId: PropTypes.string,
};

export default EditProfile;

+ 2
- 0
src/components/Cards/ProfileCard/ProfileContact/ProfileContact.js Wyświetl plik

@@ -13,6 +13,7 @@ const ProfileContact = (props) => {
return (
<ProfileContactContainer
container
isAdmin={props.isAdmin}
direction={{ xs: "column", sm: "row" }}
justifyContent={{ xs: "center", sm: "start" }}
alignItems={{ xs: "start", sm: "center" }}
@@ -47,6 +48,7 @@ ProfileContact.propTypes = {
profile: PropTypes.object,
isMyProfile: PropTypes.bool,
children: PropTypes.node,
isAdmin: PropTypes.bool,
};

export default ProfileContact;

+ 1
- 1
src/components/Cards/ProfileCard/ProfileContact/ProfileContact.styled.js Wyświetl plik

@@ -6,7 +6,7 @@ import { ReactComponent as Globe } from "../../../../assets/images/svg/globe.svg
import selectedTheme from "../../../../themes";

export const ProfileContactContainer = styled(Grid)`
padding-top: 2rem;
padding-top: ${(props) => (props.isAdmin ? `20px` : `2rem`)};
padding-bottom: 2rem;
@media (max-width: 600px) {
padding-bottom: 1rem;

+ 7
- 1
src/components/Cards/ProfileCard/ProfileMainInfo/ProfileMainInfo.js Wyświetl plik

@@ -25,6 +25,7 @@ const ProfileMainInfo = (props) => {
src={props.profile?.image}
/> */}
<AvatarImage
isAdmin={props.isAdmin}
src={getImageUrl(
props.profile?.image,
variants.profileImage,
@@ -33,7 +34,11 @@ const ProfileMainInfo = (props) => {
/>
</AvatarImageContainer>
<ProfileMainInfoGrid>
<ProfileName isMyProfile={props.isMyProfile} variant="h5">
<ProfileName
isAdmin={props.isAdmin}
isMyProfile={props.isMyProfile}
variant="h5"
>
{props.profile?.company?.name}
</ProfileName>
<ProfilePIBContainer>
@@ -51,6 +56,7 @@ ProfileMainInfo.propTypes = {
profile: PropTypes.object,
isMyProfile: PropTypes.bool,
children: PropTypes.node,
isAdmin: PropTypes.any,
};

export default ProfileMainInfo;

+ 5
- 4
src/components/Cards/ProfileCard/ProfileMainInfo/ProfileMainInfo.styled.js Wyświetl plik

@@ -14,10 +14,10 @@ export const AvatarImageContainer = styled(Grid)`
align-items: center;
`;
export const AvatarImage = styled.img`
min-height: 144px;
min-width: 144px;
width: 144px;
height: 144px;
min-height: ${(props) => (props.isAdmin ? `108px` : `144px`)};
min-width: ${(props) => (props.isAdmin ? `108px` : `144px`)};
width: ${(props) => (props.isAdmin ? `108px` : `144px`)};
height: ${(props) => (props.isAdmin ? `108px` : `144px`)};
border-radius: 100%;
@media (max-width: 600px) {
min-height: 90px;
@@ -41,6 +41,7 @@ export const ProfileName = styled(Typography)`
font-size: 24px;
font-family: ${selectedTheme.fonts.textFont};
margin-bottom: 5px;
cursor: ${(props) => props.isAdmin && `pointer`};
@media (max-width: 600px) {
font-size: 18px;
}

+ 2
- 2
src/components/MarketPlace/Header/Header.js Wyświetl plik

@@ -27,6 +27,7 @@ import {
SwapsIcon,
SwapsTitle,
TooltipInnerContainer,
UserIcon,
} from "./Header.styled";
import { ReactComponent as GridSquare } from "../../../assets/images/svg/offer-grid-square.svg";
import { ReactComponent as GridLine } from "../../../assets/images/svg/offer-grid-line.svg";
@@ -42,7 +43,6 @@ import useIsMobile from "../../../hooks/useIsMobile";
import { ArrowButton } from "../../Buttons/ArrowButton/ArrowButton";
// import { ArrowButton } from "../../Buttons/ArrowButton/ArrowButton";
import history from "../../../store/utils/history";
import { AccountCircle } from "@mui/icons-material";

const DownArrow = (props) => (
<IconStyled {...props}>
@@ -129,7 +129,7 @@ const Header = (props) => {
<>
{!isMobile ? (
<HeaderTitleContainer>
{props.users ? <AccountCircle /> : <SwapsHeaderIcon />}
{props.users ? <UserIcon /> : <SwapsHeaderIcon />}
<HeaderTitleText>
{props.users
? t("admin.users.headerTitle")

+ 10
- 1
src/components/MarketPlace/Header/Header.styled.js Wyświetl plik

@@ -6,11 +6,12 @@ import Option from "../../Select/Option/Option";
import Select from "../../Select/Select";
import { ReactComponent as Swaps } from "../../../assets/images/svg/swaps.svg";
import { ReactComponent as CategoryHeader } from "../../../assets/images/svg/category-header.svg";
import { ReactComponent as User } from "../../../assets/images/svg/user.svg";

export const HeaderWrapperContainer = styled(Box)`
display: ${(props) => (props.skeleton ? "none" : "block")};
position: relative;
top: ${props => props.isAdmin && `60px`};
top: ${(props) => props.isAdmin && `60px`};
`;

export const HeaderContainer = styled(Box)`
@@ -245,3 +246,11 @@ export const HeaderTitleText = styled(Typography)`
position: relative;
bottom: 2px;
`;
export const UserIcon = styled(User)`
position: relative;
top: 3px;
margin-right: 5px;
& path {
stroke: ${selectedTheme.colors.primaryText};
}
`;

+ 3
- 0
src/components/MarketPlace/MarketPlace.js Wyświetl plik

@@ -27,6 +27,8 @@ const MarketPlace = (props) => {
offers={offers}
toggleFilters={props.toggleFilters}
isAdmin={props.isAdmin}
isUsers={props.users}
users={props.allUsers}
/>
</MarketPlaceContainer>
);
@@ -40,6 +42,7 @@ MarketPlace.propTypes = {
toggleFilters: PropTypes.func,
isAdmin: PropTypes.bool,
users: PropTypes.bool,
allUsers: PropTypes.array,
};
MarketPlace.defaultProps = {
offers: {

+ 6
- 1
src/components/MarketPlace/Offers/HeaderMyOffers.js/HeadersMyOffers.js Wyświetl plik

@@ -47,7 +47,11 @@ const HeadersMyOffers = (props) => {
</EndIcon>
),
}}
placeholder={t("header.searchOffers")}
placeholder={
props.isUsers
? t("admin.users.searchPlaceholder")
: t("header.searchOffers")
}
onFocus={handleFocusSearch}
onBlur={handleBlurSearch}
ref={searchRef}
@@ -61,6 +65,7 @@ HeadersMyOffers.propTypes = {
handleSearch: PropTypes.func,
isAdmin: PropTypes.bool,
offers: PropTypes.any,
isUsers: PropTypes.bool,
};

export default HeadersMyOffers;

+ 21
- 12
src/components/MarketPlace/Offers/Offers.js Wyświetl plik

@@ -10,6 +10,7 @@ import { startChat } from "../../../util/helpers/chatHelper";
import OffersNotFound from "./OffersNotFound";
import HeadersMyOffers from "./HeaderMyOffers.js/HeadersMyOffers";
import SkeletonOfferCard from "../../Cards/OfferCard/SkeletonOfferCard/SkeletonOfferCard";
import BigProfileCard from "../../Cards/ProfileCard/BigProfileCard/BigProfileCard";

const Offers = (props) => {
const chats = useSelector(selectLatestChats);
@@ -44,24 +45,29 @@ const Offers = (props) => {
handleSearch={offers?.apply}
isAdmin={props?.isAdmin}
offers={offers}
isUsers={props.isUsers}
/>
)}
{offers?.allOffersToShow?.length === 0 ? (
<OffersNotFound />
) : (
<OffersContainer ref={offersRef}>
{offers?.allOffersToShow?.map((item) => {
return (
<OfferCard
key={item._id}
offer={item}
halfwidth={props?.isGrid}
messageUser={messageOneUser}
isMyOffer={item?.userId === userId || props?.isAdmin}
isAdmin={props?.isAdmin}
/>
);
})}
{props.isUsers
? props.users?.map((item) => (
<BigProfileCard key={item._id} profile={item} />
))
: offers?.allOffersToShow?.map((item) => {
return (
<OfferCard
key={item._id}
offer={item}
halfwidth={props?.isGrid}
messageUser={messageOneUser}
isMyOffer={item?.userId === userId || props?.isAdmin}
isAdmin={props?.isAdmin}
/>
);
})}
<Paging
totalElements={offers?.totalOffers}
elementsPerPage={10}
@@ -90,10 +96,13 @@ Offers.propTypes = {
offers: PropTypes.any,
toggleFilters: PropTypes.func,
isAdmin: PropTypes.bool,
isUsers: PropTypes.bool,
users: PropTypes.array,
};

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

export default Offers;

+ 4
- 21
src/components/Popovers/MyProfile/MyProfile.js Wyświetl plik

@@ -1,9 +1,5 @@
import React, { useEffect, useState } from "react";
import {
DollarIcon,
GrayButtonsContainer,
ProfileImgPIB,
} from "./MyProfile.styled";
import { GrayButtonsContainer, ProfileImgPIB } from "./MyProfile.styled";
import HeaderPopover from "../HeaderPopover/HeaderPopover";
import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
@@ -14,11 +10,10 @@ import selectedTheme from "../../../themes";
import { EyeIcon } from "../HeaderPopover/HeaderPopover.styled";
import { useHistory } from "react-router-dom";
import PropTypes from "prop-types";
import { ABOUT_PAGE } from "../../../constants/pages";
import LogoutButton from "./LogoutButton/LogoutButton";
import AboutButton from "./AboutButton/AboutButton";
import PrivacyPolicyButton from "./PrivacyPolicyButton/PrivacyPolicyButton";
import scrollConstants from "../../../constants/scrollConstants";
import PricesButton from "./PricesButton/PricesButton";

export const MyProfile = (props) => {
const { t } = useTranslation();
@@ -53,31 +48,19 @@ export const MyProfile = (props) => {
history.push(`/profile/${userId}`);
props.closePopover();
};
const seePrices = () => {
history.push({
pathname: ABOUT_PAGE,
state: {
clicked: true,
navigation: scrollConstants.about.pricesPage,
},
});
props.closePopover();
};

return (
<HeaderPopover
title={t("header.myProfile")}
items={profileAsArray}
isProfile
buttonText={t("header.checkProfile")}
buttonIcon={<EyeIcon color={selectedTheme.colors.iconYellowColor} />}
isProfile
buttonOnClick={() => seeMyProfile()}
secondButtonIcon={<DollarIcon />}
secondButtonText={t("header.prices")}
secondButtonOnClick={seePrices}
>
<GrayButtonsContainer>
<AboutButton closePopover={props.closePopover} />
<PricesButton closePopover={props.closePopover} />
<PrivacyPolicyButton closePopover={props.closePopover} />
</GrayButtonsContainer>
<LogoutButton />

+ 5
- 1
src/components/Popovers/MyProfile/MyProfile.styled.js Wyświetl plik

@@ -49,6 +49,10 @@ export const GrayButtonsContainer = styled(Box)`
`;
export const DollarIcon = styled(Dollar)`
position: relative;
left: -4px;
left: -7px;
margin-left: 10px;
& path {
stroke: ${selectedTheme.colors.borderNormal};
fill: ${selectedTheme.colors.borderNormal};
}
`;

+ 45
- 0
src/components/Popovers/MyProfile/PricesButton/PricesButton.js Wyświetl plik

@@ -0,0 +1,45 @@
import React from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
import { ABOUT_PAGE } from "../../../../constants/pages";
import { DollarIcon, GrayButton } from "../MyProfile.styled";
import { PopoverButton } from "../../HeaderPopover/HeaderPopover.styled";
import { useHistory } from "react-router-dom";
import scrollConstants from "../../../../constants/scrollConstants";

const PricesButton = (props) => {
const { t } = useTranslation();
const history = useHistory();
const seePrices = () => {
history.push({
pathname: ABOUT_PAGE,
state: {
navigation: scrollConstants.about.pricesPage,
clicked: true,
},
});
props.closePopover();
};
return (
<GrayButton>
<PopoverButton
sx={{
mr: 2,
mb: 2,
}}
variant="text"
endIcon={<DollarIcon />}
onClick={seePrices}
>
{t("header.prices")}
</PopoverButton>
</GrayButton>
);
};

PricesButton.propTypes = {
children: PropTypes.node,
closePopover: PropTypes.func,
};

export default PricesButton;

+ 8
- 0
src/components/Popovers/MyProfile/PricesButton/PricesButton.styled.js Wyświetl plik

@@ -0,0 +1,8 @@
import styled from "styled-components";
import { ReactComponent as Dollar } from "../../../../assets/images/svg/dollar.svg";

export const DollarIcon = styled(Dollar)`
position: relative;
left: -4px;
margin-left: 10px;
`;

+ 6
- 4
src/i18n/resources/rs.js Wyświetl plik

@@ -408,7 +408,7 @@ export default {
offer: "Proizvod:",
},
admin: {
login:{
login: {
welcome: "React template",
welcomeText: "Trampa sa kolegama na dohvat ruke",
emailFormat: "Nevalidan format email adrese!",
@@ -425,7 +425,9 @@ export default {
headerTitle: "Ulogujte se",
},
users: {
headerTitle: "Profili korisnika"
}
}
headerTitle: "Profili korisnika",
searchPlaceholder: "Pretražite korisnike....",
checkProfile: "Pogledaj profil"
},
},
};

+ 18
- 2
src/pages/AdminUsersPage/AdminUsersPage.js Wyświetl plik

@@ -1,9 +1,25 @@
import React from "react";
import React, { useEffect } from "react";
import PropTypes from "prop-types";
import MarketPlace from "../../components/MarketPlace/MarketPlace";
import { useDispatch, useSelector } from "react-redux";
import { selectAllProfiles } from "../../store/selectors/profileSelectors";
import { fetchAllProfiles } from "../../store/actions/profile/profileActions";

const AdminUsersPage = () => {
return <MarketPlace isAdmin myOffers users />;
const dispatch = useDispatch();
const allUsers = useSelector(selectAllProfiles);
useEffect(() => {
dispatch(fetchAllProfiles());
}, []);

return (
<MarketPlace
isAdmin
myOffers
users
allUsers={Array.isArray(allUsers) ? allUsers : []}
/>
);
};

AdminUsersPage.propTypes = {

+ 2
- 1
src/request/apiEndpoints.js Wyświetl plik

@@ -120,6 +120,7 @@ export default {
invite: "/users/invite",
getProfile: "users/",
editProfile: "users",
getAllProfiles: "users"
},
applications: {
application: "/applications/{applicationUid}",
@@ -168,7 +169,7 @@ export default {
locations: "locations",
mineOffers: "users",
removeOffer: "offers",
editOffer: "offers",
pinOffer: "offers/{id}/pin",
},
chat: {
getChat: "chats",

+ 19
- 6
src/request/offersRequest.js Wyświetl plik

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

export const attemptFetchOffers = (payload) => {
@@ -6,13 +13,14 @@ export const attemptFetchOffers = (payload) => {
return getRequest(apiEndpoints.offers.getOffers);
};
export const attemptFetchFeaturedOffers = (payload) => {
if (payload) return getRequest(apiEndpoints.offers.getFeaturedOffers + payload);
if (payload)
return getRequest(apiEndpoints.offers.getFeaturedOffers + payload);
return getRequest(apiEndpoints.offers.getOffers);
}
};
export const attemptFetchOneOffer = (payload) => {
const url = `${apiEndpoints.offers.getOneOffer}/${payload}`;
return getRequest(url);
}
const url = `${apiEndpoints.offers.getOneOffer}/${payload}`;
return getRequest(url);
};
export const attemptFetchMoreOffers = (page, payload) => {
if (payload)
return getRequest(
@@ -32,3 +40,8 @@ export const attemptRemoveOffer = (payload) => {
export const attemptEditOffer = (payload, editedData) => {
return putRequest(apiEndpoints.offers.editOffer + "/" + payload, editedData);
};
export const attemptPinOffer = (payload) => {
return patchRequest(
replaceInUrl(apiEndpoints.offers.pinOffer, { id: payload })
);
};

+ 3
- 0
src/request/profileRequest.js Wyświetl plik

@@ -4,5 +4,8 @@ import apiEndpoints from "./apiEndpoints";
export const attemptFetchProfile = (payload) =>
getRequest(apiEndpoints.users.getProfile + payload);

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

export const attemptEditProfile = (payload, requestData) =>
putRequest(apiEndpoints.users.editProfile + "/" + payload, requestData);

+ 5
- 0
src/store/actions/offers/offersActionConstants.js Wyświetl plik

@@ -72,6 +72,11 @@ export const OFFER_ADD = createFetchType(OFFER_ADD_SCOPE);
export const OFFER_ADD_SUCCESS = createSuccessType(OFFER_ADD_SCOPE);
export const OFFER_ADD_ERROR = createErrorType(OFFER_ADD_SCOPE);

export const OFFER_PIN_SCOPE = "OFFER_PIN_SCOPE";
export const OFFER_PIN = createFetchType(OFFER_PIN_SCOPE);
export const OFFER_PIN_SUCCESS = createSuccessType(OFFER_PIN_SCOPE);
export const OFFER_PIN_ERROR = createErrorType(OFFER_PIN_SCOPE);

export const OFFER_REMOVE_SCOPE = "OFFER_REMOVE_SCOPE";
export const OFFER_REMOVE = createFetchType(OFFER_REMOVE_SCOPE);
export const OFFER_REMOVE_SUCCESS = createSuccessType(OFFER_REMOVE_SCOPE);

+ 15
- 0
src/store/actions/offers/offersActions.js Wyświetl plik

@@ -37,6 +37,9 @@ import {
OFFER_EDIT_SUCCESS,
OFFER_FEATURED_PAGE_SET,
OFFER_PAGE_SET,
OFFER_PIN,
OFFER_PIN_ERROR,
OFFER_PIN_SUCCESS,
OFFER_REMOVE,
OFFER_REMOVE_ERROR,
OFFER_REMOVE_SUCCESS,
@@ -191,6 +194,18 @@ export const editOfferError = () => ({
type: OFFER_EDIT_ERROR,
});

// Pin/unpin offer
export const pinOffer = (payload) => ({
type: OFFER_PIN,
payload,
});
export const pinOfferSuccess = () => ({
type: OFFER_PIN_SUCCESS,
});
export const pinOfferError = () => ({
type: OFFER_PIN_ERROR,
});

export const setOffers = (payload) => ({
type: OFFERS_SET,
payload,

+ 6
- 0
src/store/actions/profile/profileActionConstants.js Wyświetl plik

@@ -11,6 +11,11 @@ export const PROFILE_FETCH = createFetchType(PROFILE_SCOPE);
export const PROFILE_SUCCESS = createSuccessType(PROFILE_SCOPE);
export const PROFILE_ERROR = createErrorType(PROFILE_SCOPE);

export const PROFILE_ALL_SCOPE = "PROFILE_ALL_SCOPE";
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_MINE_SCOPE = "PROFILE_MINE_SCOPE";
export const PROFILE_MINE_FETCH = createFetchType(PROFILE_MINE_SCOPE);
export const PROFILE_MINE_FETCH_SUCCESS = createSuccessType(PROFILE_MINE_SCOPE);
@@ -18,6 +23,7 @@ export const PROFILE_MINE_FETCH_ERROR = createErrorType(PROFILE_MINE_SCOPE);

export const PROFILE_SET = createSetType("PROFILE_SET");
export const PROFILE_MINE_SET = createSetType("PROFILE_MINE_SET");
export const PROFILE_ALL_SET = createSetType("PROFILE_ALL_SET");

const PROFILE_EDIT_SCOPE = "PROFILE_EDIT_SCOPE";
export const PROFILE_EDIT = createFetchType(PROFILE_EDIT_SCOPE);

+ 24
- 3
src/store/actions/profile/profileActions.js Wyświetl plik

@@ -11,6 +11,10 @@ import {
PROFILE_EDIT_SUCCESS,
PROFILE_MINE_FETCH_ERROR,
PROFILE_EDIT_ERROR,
PROFILE_ALL_FETCH,
PROFILE_ALL_SUCCESS,
PROFILE_ALL_ERROR,
PROFILE_ALL_SET,
} from "./profileActionConstants";

export const fetchProfile = (payload) => ({
@@ -26,6 +30,19 @@ export const fetchErrorProfile = (payload) => ({
payload,
});

export const fetchAllProfiles = (payload) => ({
type: PROFILE_ALL_FETCH,
payload,
});
export const fetchAllProfilesSuccess = (payload) => ({
type: PROFILE_ALL_SUCCESS,
payload,
});
export const fetchAllProfilesError = (payload) => ({
type: PROFILE_ALL_ERROR,
payload,
});

export const fetchMineProfile = () => ({
type: PROFILE_MINE_FETCH,
});
@@ -36,14 +53,14 @@ export const fetcHMineProfileError = () => ({
type: PROFILE_MINE_FETCH_ERROR,
});

export const editMineProfile = (payload) => ({
export const editProfile = (payload) => ({
type: PROFILE_EDIT,
payload,
});
export const editMineProfileSuccess = () => ({
export const editProfileSuccess = () => ({
type: PROFILE_EDIT_SUCCESS,
});
export const editMineProfileError = () => ({
export const editProfileError = () => ({
type: PROFILE_EDIT_ERROR,
});

@@ -54,6 +71,10 @@ export const setProfile = (payload) => ({
type: PROFILE_SET,
payload,
});
export const setAllProfiles = (payload) => ({
type: PROFILE_ALL_SET,
payload,
});
export const setMineProfile = (payload) => ({
type: PROFILE_MINE_SET,
payload,

+ 16
- 3
src/store/reducers/profile/profileReducer.js Wyświetl plik

@@ -1,14 +1,21 @@
import { PROFILE_CLEAR, PROFILE_MINE_SET, PROFILE_SET } from "../../actions/profile/profileActionConstants";
import {
PROFILE_ALL_SET,
PROFILE_CLEAR,
PROFILE_MINE_SET,
PROFILE_SET,
} from "../../actions/profile/profileActionConstants";
import createReducer from "../../utils/createReducer";

const initialState = {
profile: {},
mineProfile: {},
allProfiles: {},
};

export default createReducer(
{
[PROFILE_SET]: setProfile,
[PROFILE_ALL_SET]: setAllProfiles,
[PROFILE_MINE_SET]: setMineProfile,
[PROFILE_CLEAR]: clearProfile,
},
@@ -24,9 +31,15 @@ function setProfile(state, action) {
function setMineProfile(state, action) {
return {
...state,
mineProfile: action.payload
}
mineProfile: action.payload,
};
}
function clearProfile() {
return initialState;
}
function setAllProfiles(state, { payload }) {
return {
...state,
allProfiles: payload,
};
}

+ 18
- 0
src/store/saga/offersSaga.js Wyświetl plik

@@ -4,6 +4,7 @@ import {
attemptFetchFeaturedOffers,
attemptFetchOffers,
attemptFetchOneOffer,
attemptPinOffer,
attemptRemoveOffer,
} from "../../request/offersRequest";
import {
@@ -14,6 +15,7 @@ import {
OFFERS_PROFILE_FETCH,
OFFER_ADD,
OFFER_EDIT,
OFFER_PIN,
OFFER_REMOVE,
ONE_OFFER_FETCH,
} from "../actions/offers/offersActionConstants";
@@ -40,6 +42,8 @@ import {
setMineHeaderOffers,
fetchMineHeaderOffersSuccess,
fetchMineHeaderOffersError,
pinOfferSuccess,
pinOfferError,
// fetchAllOffersSuccess,
// fetchAllOffersError,
// setFeaturedOfferPage,
@@ -244,6 +248,19 @@ function* removeOffer(payload) {
console.dir(e);
}
}
function* pinOffer(payload) {
try {
const offerId = payload.payload.offerId;
yield call(attemptPinOffer, offerId);
yield put(pinOfferSuccess());
if (payload.payload.handleApiResponseSuccess) {
yield call(payload.payload.handleApiResponseSuccess);
}
} catch (e) {
yield put(pinOfferError());
console.dir(e);
}
}

function* editOffer(payload) {
try {
@@ -296,6 +313,7 @@ export default function* offersSaga() {
takeLatest(OFFER_EDIT, editOffer),
takeLatest(OFFERS_MINE_HEADER_FETCH, fetchMineHeaderOffers),
takeLatest(OFFERS_FEATURED_FETCH, fetchFeaturedOffers),
takeLatest(OFFER_PIN, pinOffer)
// takeLatest(OFFERS_ALL_FETCH, fetchAllOffers),
]);
}

+ 27
- 6
src/store/saga/profileSaga.js Wyświetl plik

@@ -1,21 +1,26 @@
import { all, call, put, takeLatest, select } from "@redux-saga/core/effects";
import {
attemptEditProfile,
attemptFetchAllProfiles,
attemptFetchProfile,
} from "../../request/profileRequest";
import {
PROFILE_FETCH,
PROFILE_MINE_FETCH,
PROFILE_EDIT,
PROFILE_ALL_FETCH,
} from "../actions/profile/profileActionConstants";
import {
editMineProfileError,
editMineProfileSuccess,
editProfileError,
editProfileSuccess,
fetchAllProfilesError,
fetchAllProfilesSuccess,
fetchErrorProfile,
fetcHMineProfileError,
fetchMineProfileSuccess,
fetchProfileSuccess,
// editMineProfile,
setAllProfiles,
// editProfile,
setMineProfile,
setProfile,
} from "../actions/profile/profileActions";
@@ -43,6 +48,16 @@ function* fetchMineProfile() {
console.dir(e);
}
}
function* fetchAllProfiles() {
try {
const data = yield call(attemptFetchAllProfiles);
if (data) yield put(setAllProfiles(data.data));
yield put(fetchAllProfilesSuccess());
} catch (e) {
yield put(fetchAllProfilesError());
console.dir(e);
}
}

function* changeMineProfile(payload) {
try {
@@ -93,14 +108,19 @@ function* changeMineProfile(payload) {
if (payload.payload.firmWebsite.toString().length !== 0)
requestBody.append("company[contacts][web]", payload.payload.firmWebsite);

const userId = yield select(selectUserId);
let userId;
if (payload.payload?.userId) {
userId = payload.payload.userId;
} else {
userId = yield select(selectUserId);
}
yield call(attemptEditProfile, userId, requestBody);
yield put(editMineProfileSuccess());
yield put(editProfileSuccess());
if (payload.payload.handleApiResponseSuccess) {
yield call(payload.payload.handleApiResponseSuccess);
}
} catch (e) {
yield put(editMineProfileError());
yield put(editProfileError());
console.dir(e);
}
}
@@ -110,5 +130,6 @@ export default function* profileSaga() {
takeLatest(PROFILE_FETCH, fetchProfile),
takeLatest(PROFILE_MINE_FETCH, fetchMineProfile),
takeLatest(PROFILE_EDIT, changeMineProfile),
takeLatest(PROFILE_ALL_FETCH, fetchAllProfiles),
]);
}

+ 17
- 13
src/store/selectors/profileSelectors.js Wyświetl plik

@@ -1,20 +1,24 @@
import { createSelector } from "reselect";

const profileSelector = (state) => state.profile
const profileSelector = (state) => state.profile;

export const selectProfileName = createSelector(
profileSelector,
(state) => state?.mineProfile?.company?.name
)
profileSelector,
(state) => state?.mineProfile?.company?.name
);
export const selectProfile = createSelector(
profileSelector,
(state) => state.profile
)
profileSelector,
(state) => state.profile
);
export const selectMineProfile = createSelector(
profileSelector,
(state) => state?.mineProfile
)
profileSelector,
(state) => state?.mineProfile
);
export const selectMineProfilePicture = createSelector(
profileSelector,
(state) => state?.mineProfile?.image
)
profileSelector,
(state) => state?.mineProfile?.image
);
export const selectAllProfiles = createSelector(
profileSelector,
(state) => state.allProfiles
);

Ładowanie…
Anuluj
Zapisz