浏览代码

Partly finished

feature/1737
Djordje Mitrovic 3 年前
父节点
当前提交
54d826422f
共有 100 个文件被更改,包括 1165 次插入25359 次删除
  1. 0
    1
      .env
  2. 1
    0
      .gitignore
  3. 42
    24897
      package-lock.json
  4. 1
    1
      package.json
  5. 6
    0
      public/index.html
  6. 4
    4
      src/App.js
  7. 8
    0
      src/assets/images/svg/bulleted-list.svg
  8. 4
    4
      src/components/About/AboutComponent.js
  9. 4
    0
      src/components/About/AboutHeader/AboutHeader.styled.js
  10. 18
    7
      src/components/About/AboutSection/AboutSection.styled.js
  11. 14
    3
      src/components/About/CheckOffersButton/CheckOffersButton.styled.js
  12. 1
    0
      src/components/Admin/Sidebar/MarketplaceButton/MarketplaceButton.js
  13. 1
    0
      src/components/Admin/Sidebar/SidebarNavigation/SidebarNavigation.js
  14. 4
    4
      src/components/Buttons/ArrowButton/ArrowButton.styled.js
  15. 1
    1
      src/components/Buttons/IconButton/IconButton.js
  16. 13
    7
      src/components/Cards/ChatCard/ChatCard.js
  17. 1
    1
      src/components/Cards/ChatCard/MobileOfferDetails/MobileOfferDetails.js
  18. 10
    3
      src/components/Cards/CreateOfferCard/CreateOffer.js
  19. 5
    1
      src/components/Cards/FilterCard/Choser/CategoryChoser/CategoryChoser.js
  20. 16
    1
      src/components/Cards/FilterCard/Choser/CompanyChoser/CompanyChoser.js
  21. 28
    4
      src/components/Cards/FilterCard/Choser/LocationChoser/LocationChoser.js
  22. 18
    7
      src/components/Cards/FilterCard/Choser/SubcategoryChoser/SubcategoryChoser.js
  23. 8
    7
      src/components/Cards/FilterCard/FilterCard.js
  24. 4
    1
      src/components/Cards/FilterCard/FilterDropdown/Checkbox/CheckboxDropdownList/CheckboxDropdownList.js
  25. 4
    0
      src/components/Cards/FilterCard/FilterDropdown/Checkbox/FilterCheckboxDropdown.js
  26. 3
    0
      src/components/Cards/FilterCard/FilterDropdown/Checkbox/FilterSubDropdown/FilterSmallDropdown/FilterSmallDropdown.js
  27. 5
    0
      src/components/Cards/FilterCard/FilterDropdown/Checkbox/FilterSubDropdown/FilterSubDropdown.js
  28. 4
    1
      src/components/Cards/FilterCard/FilterDropdown/Radio/FilterRadioDropdown.js
  29. 3
    2
      src/components/Cards/ItemDetailsCard/ImagesCarousel/ImagesCarousel.js
  30. 22
    24
      src/components/Cards/ItemDetailsCard/ItemDetailsCard.js
  31. 19
    21
      src/components/Cards/ItemDetailsCard/OfferDetails/OfferDetails.js
  32. 19
    0
      src/components/Cards/LittleOfferCard/LittleOfferCard.styled.js
  33. 5
    1
      src/components/Cards/MessageCard/MessageCard.styled.js
  34. 18
    12
      src/components/Cards/MiniChatCard/MiniChatCard.js
  35. 17
    9
      src/components/Cards/OfferCard/DeleteOffer/DeleteOffer.js
  36. 1
    1
      src/components/Cards/OfferCard/DeleteOffer/DeleteOffer.styled.js
  37. 2
    2
      src/components/Cards/OfferCard/DeleteOffer/DeleteOfferLabeledCard/DeleteOfferLabeledCard.js
  38. 5
    1
      src/components/Cards/OfferCard/DeleteOffer/DeleteOfferLabeledCard/DeleteOfferLabeledCard.styled.js
  39. 8
    4
      src/components/Cards/OfferCard/DeleteOffer/OfferDescription/OfferDescription.js
  40. 7
    1
      src/components/Cards/OfferCard/DeleteOffer/OfferDescription/OfferDescription.styled.js
  41. 8
    5
      src/components/Cards/OfferCard/OfferCard.js
  42. 6
    2
      src/components/Cards/ProfileCard/ProfileControl/ProfileControl.js
  43. 6
    6
      src/components/Cards/RequestExchangeCard/RequestExchangeCard.js
  44. 2
    0
      src/components/Cards/RequestExchangeCard/RequestExchangeCard.styled.js
  45. 31
    12
      src/components/Cards/RequestExchangeCard/RequestExchangeMessage/RequestExchangeMessage.js
  46. 14
    14
      src/components/Cards/UserReviewsCard/UserReviewsCard.js
  47. 3
    0
      src/components/Cards/UserReviewsCard/UserReviewsSingleCard/RemoveButton/RemoveButton.js
  48. 15
    9
      src/components/Cards/UserReviewsCard/UserReviewsSingleCard/RemoveButton/RemoveButton.styled.js
  49. 3
    1
      src/components/Cards/UserReviewsCard/UserReviewsSingleCard/ReviewQuote/ReviewQuote.styled.js
  50. 2
    0
      src/components/Cards/UserReviewsCard/UserReviewsSingleCard/UserReviewsSingleCard.js
  51. 3
    2
      src/components/ChatColumn/ChatColumn.js
  52. 4
    8
      src/components/CreateReview/CreateReview.js
  53. 15
    7
      src/components/CreateReview/CreateReview.styled.js
  54. 2
    1
      src/components/CreateReview/FirstStep/FirstStepCreateReview.js
  55. 5
    3
      src/components/CreateReview/FirstStep/FirstStepCreateReview.styled.js
  56. 13
    13
      src/components/CreateReview/SecondStep/SecondStepCreateReview.js
  57. 13
    1
      src/components/CreateReview/SecondStep/SecondStepCreateReview.styled.js
  58. 6
    0
      src/components/CreateReview/ThirdStep/ThirdStepCreateReview.js
  59. 48
    43
      src/components/DirectChat/DirectChat.js
  60. 14
    10
      src/components/DirectChat/DirectChatContent/DirectChatContent.js
  61. 1
    1
      src/components/DirectChat/DirectChatContent/DirectChatContent.styled.js
  62. 19
    12
      src/components/DirectChat/DirectChatContent/DirectChatContentHeader/DirectChatContentHeader.js
  63. 7
    0
      src/components/DirectChat/DirectChatContent/DirectChatContentHeader/DirectChatContentHeader.styled.js
  64. 28
    11
      src/components/DirectChat/DirectChatHeader/DirectChatHeader.js
  65. 20
    15
      src/components/DirectChat/DirectChatNewMessage/DirectChatNewMessage.js
  66. 11
    7
      src/components/DirectChat/MiniChatColumn/MiniChatColumn.js
  67. 6
    2
      src/components/Footer/AboutFooter.styled.js
  68. 4
    0
      src/components/Header/AboutHeader/AboutHeader.styled.js
  69. 7
    1
      src/components/Header/Drawer/Buttons/MyProfileButton/MyProfileButton.js
  70. 20
    3
      src/components/Header/Header.js
  71. 29
    0
      src/components/Header/Header.styled.js
  72. 2
    0
      src/components/Header/SearchInput/SearchInput.js
  73. 12
    6
      src/components/ItemDetails/ItemDetails.js
  74. 36
    11
      src/components/ItemDetails/ItemDetailsHeaderCard/ItemDetailsHeaderCard.js
  75. 1
    1
      src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/Category/CategoryDetail.js
  76. 1
    1
      src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/PIB/PIBDetail.js
  77. 5
    5
      src/components/ItemDetails/ItemDetailsHeaderCard/StatisticDetails/StatisticDetails.js
  78. 8
    5
      src/components/MarketPlace/Header/Header.js
  79. 3
    0
      src/components/MarketPlace/Header/TooltipHeader/TooltipHeader.js
  80. 10
    2
      src/components/MarketPlace/Offers/Offers.js
  81. 6
    2
      src/components/MarketPlace/Offers/OffersList/OffersList.js
  82. 1
    1
      src/components/Modals/EditCategory/EditCategory.js
  83. 23
    9
      src/components/Popovers/MyMessages/MyMessages.js
  84. 7
    2
      src/components/Popovers/MyPosts/MyPosts.js
  85. 37
    2
      src/components/Prices/Plan/Plan.styled.js
  86. 13
    0
      src/components/Prices/PricesComponent.styled.js
  87. 4
    0
      src/components/PrivacyPolicy/PrivacyPolicyComponent.styled.js
  88. 11
    4
      src/components/Profile/ProfileOffers/ProfileOffers.js
  89. 4
    4
      src/components/RichTextComponent/BlockButton/BlockButton.js
  90. 27
    0
      src/components/RichTextComponent/BlockButton/BlockButton.styled.js
  91. 43
    0
      src/components/RichTextComponent/ColorButton/ColorButton.js
  92. 7
    4
      src/components/RichTextComponent/MarkButton/MarkButton.js
  93. 27
    0
      src/components/RichTextComponent/MarkButton/MarkButton.styled.js
  94. 80
    60
      src/components/RichTextComponent/RichTextComponent.js
  95. 46
    0
      src/components/RichTextComponent/RichTextComponent.styled.js
  96. 5
    4
      src/components/RichTextComponent/RichTextElement/RichTextElement.js
  97. 9
    0
      src/components/RichTextComponent/RichTextElement/RichTextElement.styled.js
  98. 1
    1
      src/components/RichTextComponent/RichTextLeaf/RichTextLeaf.js
  99. 17
    13
      src/components/UserReviews/ReviewsSorting/ReviewsSorting.js
  100. 0
    0
      src/components/UserReviews/UserReviews.js

+ 0
- 1
.env 查看文件

REACT_APP_BASE_API_URL=http://localhost:3001/

+ 1
- 0
.gitignore 查看文件



# misc # misc
.DS_Store .DS_Store
.env
.env.local .env.local
.env.development.local .env.development.local
.env.test.local .env.test.local

+ 42
- 24897
package-lock.json
文件差异内容过多而无法显示
查看文件


+ 1
- 1
package.json 查看文件

{ {
"name": "web", "name": "web",
"version": "4.0.9",
"version": "4.0.10",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@emotion/react": "^11.5.0", "@emotion/react": "^11.5.0",

+ 6
- 0
public/index.html 查看文件

href="https://fonts.googleapis.com/css?family=Poppins&display=swap" href="https://fonts.googleapis.com/css?family=Poppins&display=swap"
onload="this.onload=null;this.rel='stylesheet';this.type='text/css'" onload="this.onload=null;this.rel='stylesheet';this.type='text/css'"
/> />
<link
rel="preload"
as="style"
href="https://fonts.googleapis.com/css?family=Source+Code+Pro:400,700&display=swap"
onload="this.onload=null;this.rel='stylesheet';this.type='text/css'"
/>
<link <link
style style
rel="preload" rel="preload"

+ 4
- 4
src/App.js 查看文件

import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import i18next from "i18next"; import i18next from "i18next";


import { selectUserId } from "./store/selectors/loginSelectors";
import { selectJwtToken, selectUserId } from "./store/selectors/loginSelectors";
import history from "./store/utils/history"; import history from "./store/utils/history";
import AppRoutes from "./AppRoutes"; import AppRoutes from "./AppRoutes";
import { socketInit } from "./socket/socket"; import { socketInit } from "./socket/socket";


const App = () => { const App = () => {
const userId = useSelector(selectUserId); const userId = useSelector(selectUserId);
const token = useSelector(selectJwtToken);


useEffect(() => { useEffect(() => {
socketInit(userId);
}, [userId]);

socketInit(userId, token);
}, [userId, token]);
return ( return (
<Router history={history}> <Router history={history}>
<Helmet> <Helmet>

+ 8
- 0
src/assets/images/svg/bulleted-list.svg 查看文件

<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 4.5H15.75" stroke="#667080" stroke-width="1.24" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 9H15.75" stroke="#667080" stroke-width="1.24" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 13.5H15.75" stroke="#667080" stroke-width="1.24" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.25 4.5H2.2575" stroke="#667080" stroke-width="1.24" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.25 9H2.2575" stroke="#667080" stroke-width="1.24" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.25 13.5H2.2575" stroke="#667080" stroke-width="1.24" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

+ 4
- 4
src/components/About/AboutComponent.js 查看文件

import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import SectionImage1 from "../../assets/images/about/about-1.png"; import SectionImage1 from "../../assets/images/about/about-1.png";
import SectionImage2 from "../../assets/images/about/about-2.png"; import SectionImage2 from "../../assets/images/about/about-2.png";
import CheckOffersButton from "./CheckOffersButton/CheckOffersButton";
// import CheckOffersButton from "./CheckOffersButton/CheckOffersButton";
import { AboutComponentContainer } from "./AboutComponent.styled"; import { AboutComponentContainer } from "./AboutComponent.styled";
import useIsMobile from "../../hooks/useIsMobile";
// import useIsMobile from "../../hooks/useIsMobile";


const AboutComponent = forwardRef((props, ref) => { const AboutComponent = forwardRef((props, ref) => {
const { isMobile } = useIsMobile();
// const { isMobile } = useIsMobile();
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<AboutComponentContainer ref={ref}> <AboutComponentContainer ref={ref}>
image={SectionImage2} image={SectionImage2}
reverse reverse
/> />
{!isMobile && <CheckOffersButton />}
{/* <CheckOffersButton /> */}
</AboutComponentContainer> </AboutComponentContainer>
); );
}); });

+ 4
- 0
src/components/About/AboutHeader/AboutHeader.styled.js 查看文件

export const AboutHeaderContainer = styled(Box)` export const AboutHeaderContainer = styled(Box)`
margin: 72px; margin: 72px;


@media (max-width: 1430px) {
margin: 45px;
}

@media (max-width: 1200px) { @media (max-width: 1200px) {
margin: 36px; margin: 36px;
} }

+ 18
- 7
src/components/About/AboutSection/AboutSection.styled.js 查看文件

flex-direction: ${(props) => (props.reverse ? "row-reverse" : "row")}; flex-direction: ${(props) => (props.reverse ? "row-reverse" : "row")};
gap: 144px; gap: 144px;
margin-bottom: 72px; margin-bottom: 72px;
${(props) => props.reverse && `text-align: right;`}

@media (max-width: 1430px) {
${(props) =>
props.reverse
? `
margin-right: 45px
`
: `margin-left: 45px`};
}


@media (max-width: 1069px) { @media (max-width: 1069px) {
flex-direction: column; flex-direction: column;
margin-bottom: 41px;
} }


@media (max-width: 1200px) { @media (max-width: 1200px) {
} }


@media (max-width: 1319px) { @media (max-width: 1319px) {
gap: 54px;
gap: 40px;
} }


@media (max-width: 600px) { @media (max-width: 600px) {
line-height: 22px; line-height: 22px;
color: ${selectedTheme.colors.primaryGrayText}; color: ${selectedTheme.colors.primaryGrayText};


@media (max-width: 1069px) {
${(props) => props.reverse && `margin-left: 36px;`}
@media (min-width: 1069px) and (max-width: 1230px) {
${(props) => props.reverse && `padding-bottom: 50px;`}
} }


@media (max-width: 1160px) {
${(props) => props.reverse && `padding-bottom: 20px;`}
@media (max-width: 1069px) {
${(props) => props.reverse && `margin-left: 36px;`}
} }


@media (max-width: 600px) { @media (max-width: 600px) {
object-fit: cover; object-fit: cover;


@media (max-width: 1069px) { @media (max-width: 1069px) {
width: 100%;
${(props) => props.reverse && `padding-bottom: 36px`}
width: 80%;
${(props) => !props.reverse && `align-self: end;`}
} }


@media (max-width: 600px) { @media (max-width: 600px) {

+ 14
- 3
src/components/About/CheckOffersButton/CheckOffersButton.styled.js 查看文件

background-color: ${selectedTheme.colors.primaryPurple} !important; background-color: ${selectedTheme.colors.primaryPurple} !important;
} }


@media (max-width: 1160px) {
bottom: -16px;
@media (max-width: 1430px) {
right: 22px;
}

@media (max-width: 1230px) {
bottom: 0;
} }


@media (max-width: 1200px) { @media (max-width: 1200px) {
right: 16px;
bottom: 0;
right: 18px;
}

@media (max-width: 1069px) {
position: relative;
right: 0;
align-self: flex-end;
} }


@media (max-width: 600px) { @media (max-width: 600px) {

+ 1
- 0
src/components/Admin/Sidebar/MarketplaceButton/MarketplaceButton.js 查看文件

MarketplaceIcon, MarketplaceIcon,
} from "./MarketplaceButton.styled"; } from "./MarketplaceButton.styled";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import history from "../../../../store/utils/history";


const MarketplaceButton = () => { const MarketplaceButton = () => {
const { t } = useTranslation(); const { t } = useTranslation();

+ 1
- 0
src/components/Admin/Sidebar/SidebarNavigation/SidebarNavigation.js 查看文件

import { isInRoute, routeMatches } from "../../../../util/helpers/routeHelpers"; import { isInRoute, routeMatches } from "../../../../util/helpers/routeHelpers";
import { ADMIN_HOME_PAGE, ADMIN_USERS_PAGE } from "../../../../constants/pages"; import { ADMIN_HOME_PAGE, ADMIN_USERS_PAGE } from "../../../../constants/pages";
import { ADMIN_NAVIGATION } from "../../../../constants/adminNavigation"; import { ADMIN_NAVIGATION } from "../../../../constants/adminNavigation";
import history from "../../../../store/utils/history";


const SidebarNavigation = () => { const SidebarNavigation = () => {
const { t } = useTranslation(); const { t } = useTranslation();

+ 4
- 4
src/components/Buttons/ArrowButton/ArrowButton.styled.js 查看文件

import { ReactComponent as DownArrow } from "../../../assets/images/svg/arrow-down.svg"; import { ReactComponent as DownArrow } from "../../../assets/images/svg/arrow-down.svg";
import styled from "styled-components";
import styled, { css } from "styled-components";
import selectedTheme from "../../../themes"; import selectedTheme from "../../../themes";
import { IconButton } from "../IconButton/IconButton"; import { IconButton } from "../IconButton/IconButton";


`} `}
width: 18px; width: 18px;
height: 18px; height: 18px;
/* position: relative;
position: relative;
top: 1px; top: 1px;
left: 1px; */
left: 1px;
& path { & path {
${(props) => ${(props) =>
props.disabled && props.disabled &&
} }
${(props) => ${(props) =>
props.disabled && props.disabled &&
`
css`
border 1px solid ${selectedTheme.colors.iconStrokeDisabledColor} !important; border 1px solid ${selectedTheme.colors.iconStrokeDisabledColor} !important;
&:hover { &:hover {
background-color: inherit; background-color: inherit;

+ 1
- 1
src/components/Buttons/IconButton/IconButton.js 查看文件

onClick={props.onClick} onClick={props.onClick}
sx={props.style} sx={props.style}
iconcolor={props.iconColor} iconcolor={props.iconColor}
{...props}
// {...props}
> >
{props.children} {props.children}
</IconButtonStyled> </IconButtonStyled>

+ 13
- 7
src/components/Cards/ChatCard/ChatCard.js 查看文件

import useIsMobile from "../../../hooks/useIsMobile"; import useIsMobile from "../../../hooks/useIsMobile";
import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter"; import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter";
import BlockedProfile from "../ProfileCard/BlockedProfile/BlockedProfile"; import BlockedProfile from "../ProfileCard/BlockedProfile/BlockedProfile";
import { useSelector } from "react-redux";
import { selectUserId } from "../../../store/selectors/loginSelectors";


// Chat card is shown only on mobile phones (route /messages) // Chat card is shown only on mobile phones (route /messages)
const ChatCard = (props) => { const ChatCard = (props) => {
const { isMobile } = useIsMobile(); const { isMobile } = useIsMobile();
const userId = useSelector(selectUserId);


const chat = useMemo(() => { const chat = useMemo(() => {
return props.chat; return props.chat;
}, [props.chat]); }, [props.chat]);


const routeToItem = () => { const routeToItem = () => {
props.navigateToChat(chat?.chat?._id);
props.navigateToChat(chat?._id);
}; };
const interlocutor = useMemo(() => {
return userId === chat?.participants[0]._id ? 1 : 0;
}, [userId, chat]);


return ( return (
<ChatCardContainer onClick={routeToItem}> <ChatCardContainer onClick={routeToItem}>
<UserImgWrapper> <UserImgWrapper>
<UserImage <UserImage
src={getImageUrl( src={getImageUrl(
chat?.interlocutorData?.image,
chat?.participants[interlocutor].image,
variants.chatCard, variants.chatCard,
isMobile isMobile
)} )}
/> />
</UserImgWrapper> </UserImgWrapper>
<ChatInfo isBlocked={props.chat.interlocutorData?._blocked}>
<ChatInfo isBlocked={props.chat.participants[interlocutor]?._blocked}>
<ProfileNameContainer> <ProfileNameContainer>
<UserName>{chat?.interlocutorData?.name}</UserName>
{(props.chat.interlocutorData?._blocked ||
props.chat.interlocutorData?._deleted) && (
<UserName>{chat?.participants[interlocutor]?.company?.name}</UserName>
{(props.chat?.participants[interlocutor]?._blocked ||
props.chat?.participants[interlocutor]?._deleted) && (
<BlockedProfile <BlockedProfile
redText redText
chatCard chatCard
shortText shortText
aboveTitle aboveTitle
hideIcon hideIcon
deleted={props.chat.interlocutorData?._deleted}
deleted={props.chat?.participants[interlocutor]?._deleted}
/> />
)} )}
</ProfileNameContainer> </ProfileNameContainer>

+ 1
- 1
src/components/Cards/ChatCard/MobileOfferDetails/MobileOfferDetails.js 查看文件

return ( return (
<OfferCardContainerMobile> <OfferCardContainerMobile>
<OfferTextMobile>{t("messages.cardProduct")}</OfferTextMobile> <OfferTextMobile>{t("messages.cardProduct")}</OfferTextMobile>
<OfferTitleMobile>{props.chat?.offerData?.name}</OfferTitleMobile>
<OfferTitleMobile>{props.chat?.offer?.name}</OfferTitleMobile>
</OfferCardContainerMobile> </OfferCardContainerMobile>
); );
}; };

+ 10
- 3
src/components/Cards/CreateOfferCard/CreateOffer.js 查看文件

ITEM_DETAILS_PAGE, ITEM_DETAILS_PAGE,
PROFILE_PAGE, PROFILE_PAGE,
} from "../../../constants/pages"; } from "../../../constants/pages";
import { dynamicRouteMatches, replaceInRoute } from "../../../util/helpers/routeHelpers";
import {
dynamicRouteMatches,
replaceInRoute,
} from "../../../util/helpers/routeHelpers";
import { selectIsLoadingByActionType } from "../../../store/selectors/loadingSelectors"; import { selectIsLoadingByActionType } from "../../../store/selectors/loadingSelectors";
import { import {
OFFER_ADD_SCOPE, OFFER_ADD_SCOPE,
if (editOffer) { if (editOffer) {
if (routeMatches(BASE_PAGE) || routeMatches(HOME_PAGE)) if (routeMatches(BASE_PAGE) || routeMatches(HOME_PAGE))
dispatch(fetchOffers({ queryString })); dispatch(fetchOffers({ queryString }));
if (dynamicRouteMatches(PROFILE_PAGE) || dynamicRouteMatches(ADMIN_SINGLE_USER_PAGE))
if (isAdmin) dispatch(fetchProfileOffers(customUserId));
if (
dynamicRouteMatches(PROFILE_PAGE) ||
dynamicRouteMatches(ADMIN_SINGLE_USER_PAGE)
)
if (isAdmin)
dispatch(fetchProfileOffers({ idProfile: customUserId, isAdmin }));
else dispatch(fetchProfileOffers(userId)); else dispatch(fetchProfileOffers(userId));


if ( if (

+ 5
- 1
src/components/Cards/FilterCard/Choser/CategoryChoser/CategoryChoser.js 查看文件

const handleSelectCategory = (category) => { const handleSelectCategory = (category) => {
filters.category.setSelectedCategory(category); filters.category.setSelectedCategory(category);
filters.subcategory.setSelectedSubcategory({}); filters.subcategory.setSelectedSubcategory({});
props.offers.applyFilters();
}; };
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
closeSection: () => { closeSection: () => {


useEffect(() => { useEffect(() => {
if ( if (
!filters.category.selectedCategoryLocally ||
(filters.category.selectedCategoryLocally &&
!("_id" in filters.category.selectedCategoryLocally)) ||
(filters.category.selectedCategoryLocally?._id === 0 && !isOpened) (filters.category.selectedCategoryLocally?._id === 0 && !isOpened)
) { ) {
setIsOpened(false); setIsOpened(false);
setSelected={handleSelectCategory} setSelected={handleSelectCategory}
selected={filters.category.selectedCategoryLocally} selected={filters.category.selectedCategoryLocally}
firstOption={firstCategoryOption} firstOption={firstCategoryOption}
offers={props.offers}
/> />
); );
}); });


CategoryChoser.propTypes = { CategoryChoser.propTypes = {
filters: PropTypes.any, filters: PropTypes.any,
offers: PropTypes.any,
}; };


export default CategoryChoser; export default CategoryChoser;

+ 16
- 1
src/components/Cards/FilterCard/Choser/CompanyChoser/CompanyChoser.js 查看文件



const CompanyChoser = forwardRef((props, ref) => { const CompanyChoser = forwardRef((props, ref) => {
const [isOpened, setIsOpened] = useState(false); const [isOpened, setIsOpened] = useState(false);
const [appliedFilters, setAppliedFilters] = useState(false);
const filters = props.filters; const filters = props.filters;
const { t } = useTranslation(); const { t } = useTranslation();


} }
}, [filters.companies.selectedCompaniesLocally]); }, [filters.companies.selectedCompaniesLocally]);


useEffect(() => {
if (appliedFilters) {
props?.offers?.apply();
setAppliedFilters(false);
}
}, [filters.companies.selectedCompaniesLocally]);

const handleSetItemsSelected = (items) => {
filters.companies.setSelectedCompanies(items);
setAppliedFilters(true);
};

return ( return (
<FilterSubDropdown <FilterSubDropdown
searchPlaceholder={t("filters.company.placeholder")} searchPlaceholder={t("filters.company.placeholder")}
handleOpen={() => setIsOpened((prevIsOpened) => !prevIsOpened)} handleOpen={() => setIsOpened((prevIsOpened) => !prevIsOpened)}
icon={<CompanyIcon />} icon={<CompanyIcon />}
title={t("filters.company.title")} title={t("filters.company.title")}
setItemsSelected={filters.companies.setSelectedCompanies}
setItemsSelected={handleSetItemsSelected}
companies companies
offers={props.offers}
/> />
); );
}); });


CompanyChoser.propTypes = { CompanyChoser.propTypes = {
filters: PropTypes.any, filters: PropTypes.any,
offers: PropTypes.any,
}; };


export default CompanyChoser; export default CompanyChoser;

+ 28
- 4
src/components/Cards/FilterCard/Choser/LocationChoser/LocationChoser.js 查看文件

import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from "react";
import React, {
forwardRef,
useEffect,
useImperativeHandle,
useMemo,
useState,
} from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import FilterCheckboxDropdown from "../../FilterDropdown/Checkbox/FilterCheckboxDropdown"; import FilterCheckboxDropdown from "../../FilterDropdown/Checkbox/FilterCheckboxDropdown";
import { LocationIcon } from "./LocationChoser.styled"; import { LocationIcon } from "./LocationChoser.styled";


const LocationChoser = forwardRef((props, ref) => { const LocationChoser = forwardRef((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [appliedFilters, setAppliedFilters] = useState(false);
const [isOpened, setIsOpened] = useState(false); const [isOpened, setIsOpened] = useState(false);
const filters = props.filters; const filters = props.filters;


const allLocations = useMemo(() => filters.locations.allLocations || [], [filters.locations])
const allLocations = useMemo(
() => filters.locations.allLocations || [],
[filters.locations]
);


useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
closeSection: () => { closeSection: () => {
if (filters.locations.selectedLocationsLocally.length > 0 && !isOpened) { if (filters.locations.selectedLocationsLocally.length > 0 && !isOpened) {
setIsOpened(true); setIsOpened(true);
} }
}, [filters.locations.selectedLocationsLocally])
}, [filters.locations.selectedLocationsLocally]);

useEffect(() => {
if (appliedFilters) {
props?.offers?.apply();
setAppliedFilters(false);
}
}, [appliedFilters]);

const handleSetItemsSelected = (items) => {
filters.locations.setSelectedLocations(items);
setAppliedFilters(true);
};
return ( return (
<FilterCheckboxDropdown <FilterCheckboxDropdown
searchPlaceholder={t("filters.location.placeholder")} searchPlaceholder={t("filters.location.placeholder")}
handleOpen={() => setIsOpened((prevIsOpened) => !prevIsOpened)} handleOpen={() => setIsOpened((prevIsOpened) => !prevIsOpened)}
icon={<LocationIcon />} icon={<LocationIcon />}
title={t("filters.location.title")} title={t("filters.location.title")}
setItemsSelected={filters.locations.setSelectedLocations}
setItemsSelected={handleSetItemsSelected}
offers={props.offers}
/> />
); );
}); });


LocationChoser.propTypes = { LocationChoser.propTypes = {
filters: PropTypes.any, filters: PropTypes.any,
offers: PropTypes.any,
}; };


export default LocationChoser; export default LocationChoser;

+ 18
- 7
src/components/Cards/FilterCard/Choser/SubcategoryChoser/SubcategoryChoser.js 查看文件

}, },
})); }));


const handleSelectSubcategory = (subcategory) => {
filters.subcategory.setSelectedSubcategory(subcategory, !props.myOffers);
// if (props.myOffers) props.offers?.applyFilters();
};

useEffect(() => { useEffect(() => {
if (props?.queryStringHook?.isInitiallyLoaded || props.isMyOffers) {
if (props?.queryStringHook?.isInitiallyLoaded || props.myOffers) {
if ( if (
!filters.category.selectedCategoryLocally ||
filters.category.selectedCategoryLocally?._id === 0
(filters.category.selectedCategoryLocally &&
!("_id" in filters.category.selectedCategoryLocally)) ||
filters.category.selectedCategoryLocally?._id === 0 ||
!filters.category.selectedCategoryLocally
) { ) {
setIsOpened(false); setIsOpened(false);
setIsDisabled(true); setIsDisabled(true);
} }
} else { } else {
if ( if (
!filters.subcategory.selectedSubcategoryLocally ||
(filters.subcategory.selectedSubcategoryLocally &&
!("_id" in filters.subcategory.selectedSubcategoryLocally)) ||
filters.subcategory.selectedSubcategoryLocally?._id === 0 filters.subcategory.selectedSubcategoryLocally?._id === 0
) { ) {
setIsOpened(false); setIsOpened(false);
if ( if (
!filters.category.selectedCategoryLocally ||
(filters.category.selectedCategoryLocally &&
!("_id" in filters.category.selectedCategoryLocally)) ||
filters.category.selectedCategoryLocally?._id === 0 filters.category.selectedCategoryLocally?._id === 0
) { ) {
setIsDisabled(true); setIsDisabled(true);
: t("filters.subcategories.title") : t("filters.subcategories.title")
} }
searchPlaceholder={t("filters.subcategories.placeholder")} searchPlaceholder={t("filters.subcategories.placeholder")}
setSelected={filters.subcategory.setSelectedSubcategory}
setSelected={handleSelectSubcategory}
selected={filters.subcategory.selectedSubcategoryLocally} selected={filters.subcategory.selectedSubcategoryLocally}
open={isOpened} open={isOpened}
disabled={isDisabled} disabled={isDisabled}
handleOpen={handleOpen} handleOpen={handleOpen}
firstOption={filters.subcategory.initialOption} firstOption={filters.subcategory.initialOption}
offers={props.offers}
/> />
); );
}); });
filters: PropTypes.any, filters: PropTypes.any,
categoryOpened: PropTypes.bool, categoryOpened: PropTypes.bool,
queryStringHook: PropTypes.any, queryStringHook: PropTypes.any,
isMyOffers: PropTypes.bool,
myOffers: PropTypes.bool,
offers: PropTypes.any,
}; };


export default SubcategoryChoser; export default SubcategoryChoser;

+ 8
- 7
src/components/Cards/FilterCard/FilterCard.js 查看文件

import { ContentContainer, FilterCardContainer } from "./FilterCard.styled"; import { ContentContainer, FilterCardContainer } from "./FilterCard.styled";
import HeaderBack from "../../ItemDetails/Header/Header"; import HeaderBack from "../../ItemDetails/Header/Header";
import FilterHeader from "./FilterHeader/FilterHeader"; import FilterHeader from "./FilterHeader/FilterHeader";
import FilterFooter from "./FilterFooter/FilterFooter";
// import FilterFooter from "./FilterFooter/FilterFooter";
import CategoryChoser from "./Choser/CategoryChoser/CategoryChoser"; import CategoryChoser from "./Choser/CategoryChoser/CategoryChoser";
import SubcategoryChoser from "./Choser/SubcategoryChoser/SubcategoryChoser"; import SubcategoryChoser from "./Choser/SubcategoryChoser/SubcategoryChoser";
import LocationChoser from "./Choser/LocationChoser/LocationChoser"; import LocationChoser from "./Choser/LocationChoser/LocationChoser";


<ContentContainer> <ContentContainer>
{/* Categories */} {/* Categories */}
<CategoryChoser filters={filters} ref={categoryRef} />
<CategoryChoser filters={filters} ref={categoryRef} offers={offers} />


{/* Subcategories */} {/* Subcategories */}
<SubcategoryChoser <SubcategoryChoser
queryStringHook={offers.queryStringHook} queryStringHook={offers.queryStringHook}
ref={subcategoryRef} ref={subcategoryRef}
categoryOpened={categoryRef.current?.isOpened} categoryOpened={categoryRef.current?.isOpened}
isMyOffers={props.myOffers}
myOffers={props.myOffers}
offers={offers}
/> />


{/* Locations */} {/* Locations */}
{!props.myOffers && ( {!props.myOffers && (
<LocationChoser filters={filters} ref={locationRef} />
<LocationChoser filters={filters} ref={locationRef} offers={offers} />
)} )}


{/* Companies */} {/* Companies */}
{!props.myOffers && ( {!props.myOffers && (
<CompanyChoser filters={filters} ref={companyRef} />
<CompanyChoser filters={filters} ref={companyRef} offers={offers} />
)} )}
</ContentContainer> </ContentContainer>


<FilterFooter
{/* <FilterFooter
toggleFilters={props.toggleFilters} toggleFilters={props.toggleFilters}
filters={offers} filters={offers}
closeAllSections={closeAllSections} closeAllSections={closeAllSections}
isMyOffers={props.myOffers} isMyOffers={props.myOffers}
/>
/> */}
</FilterCardContainer> </FilterCardContainer>
); );
}; };

+ 4
- 1
src/components/Cards/FilterCard/FilterDropdown/Checkbox/CheckboxDropdownList/CheckboxDropdownList.js 查看文件

const data = props.data; const data = props.data;
const [isOpened, setIsOpened] = useState(false); const [isOpened, setIsOpened] = useState(false);
const handleDelete = (item) => { const handleDelete = (item) => {
props.setItemsSelected([...props.filters.filter((p) => p !== item)]);
console.log(item);
console.log(props.filters)
props.setItemsSelected([...props.filters.filter((p) => p?._id !== item?._id)]);
}; };
const handleOpen = () => { const handleOpen = () => {
setIsOpened((prevState) => !prevState); setIsOpened((prevState) => !prevState);
open: PropTypes.bool, open: PropTypes.bool,
handleOpen: PropTypes.func, handleOpen: PropTypes.func,
companies: PropTypes.bool, companies: PropTypes.bool,
offers: PropTypes.any,
}; };


export default CheckboxDropdownList; export default CheckboxDropdownList;

+ 4
- 0
src/components/Cards/FilterCard/FilterDropdown/Checkbox/FilterCheckboxDropdown.js 查看文件

(p) => p?.city?.toString() !== item?.city?.toString() (p) => p?.city?.toString() !== item?.city?.toString()
), ),
]); ]);
props.offers.applyFilters();
} else { } else {
props.setItemsSelected([...props.filters, item]); props.setItemsSelected([...props.filters, item]);
props.offers.applyFilters();
} }
} }
}; };
handleOpen={handleOpen} handleOpen={handleOpen}
setItemsSelected={props.setItemsSelected} setItemsSelected={props.setItemsSelected}
companies={props.companies} companies={props.companies}
offers={props.offers}
> >
{(isOpened || props?.open) && {(isOpened || props?.open) &&
dataToShow.map((item) => { dataToShow.map((item) => {
open: PropTypes.bool, open: PropTypes.bool,
handleOpen: PropTypes.func, handleOpen: PropTypes.func,
companies: PropTypes.bool, companies: PropTypes.bool,
offers: PropTypes.any,
}; };
FilterCheckboxDropdown.defaultProps = { FilterCheckboxDropdown.defaultProps = {
oneValueAllowed: false, oneValueAllowed: false,

+ 3
- 0
src/components/Cards/FilterCard/FilterDropdown/Checkbox/FilterSubDropdown/FilterSmallDropdown/FilterSmallDropdown.js 查看文件

(p) => p?.companyName?.toString() !== item?.companyName?.toString() (p) => p?.companyName?.toString() !== item?.companyName?.toString()
), ),
]); ]);
props.offers.applyFilters();
} else { } else {
props.setItemsSelected([...props.filters, item]); props.setItemsSelected([...props.filters, item]);
props.offers.applyFilters();
} }
} }
}; };
companies: PropTypes.bool, companies: PropTypes.bool,
letters: PropTypes.any, letters: PropTypes.any,
dataToShow: PropTypes.array, dataToShow: PropTypes.array,
offers: PropTypes.any,
}; };
FilterSmallDropdown.defaultProps = { FilterSmallDropdown.defaultProps = {
oneValueAllowed: false, oneValueAllowed: false,

+ 5
- 0
src/components/Cards/FilterCard/FilterDropdown/Checkbox/FilterSubDropdown/FilterSubDropdown.js 查看文件

filters={props.filters} filters={props.filters}
icon={props.icon} icon={props.icon}
data={data} data={data}
offers={props?.offers}
searchPlaceholder={props.searchPlaceholder} searchPlaceholder={props.searchPlaceholder}
open={props?.open !== undefined ? props.open : isOpened} open={props?.open !== undefined ? props.open : isOpened}
handleOpen={handleOpen} handleOpen={handleOpen}
filters={props.filters} filters={props.filters}
setItemsSelected={props.setItemsSelected} setItemsSelected={props.setItemsSelected}
companies={props.companies} companies={props.companies}
offers={props.offers}
/> />
<FilterSmallDropdown <FilterSmallDropdown
letters={t("filters.company.secondSort")} letters={t("filters.company.secondSort")}
filters={props.filters} filters={props.filters}
setItemsSelected={props.setItemsSelected} setItemsSelected={props.setItemsSelected}
companies={props.companies} companies={props.companies}
offers={props.offers}
/> />
<FilterSmallDropdown <FilterSmallDropdown
letters={t("filters.company.thirdSort")} letters={t("filters.company.thirdSort")}
filters={props.filters} filters={props.filters}
setItemsSelected={props.setItemsSelected} setItemsSelected={props.setItemsSelected}
companies={props.companies} companies={props.companies}
offers={props.offers}
/> />
</> </>
)} )}
open: PropTypes.bool, open: PropTypes.bool,
handleOpen: PropTypes.func, handleOpen: PropTypes.func,
companies: PropTypes.bool, companies: PropTypes.bool,
offers: PropTypes.any,
}; };
FilterSubDropdown.defaultProps = { FilterSubDropdown.defaultProps = {
oneValueAllowed: false, oneValueAllowed: false,

+ 4
- 1
src/components/Cards/FilterCard/FilterDropdown/Radio/FilterRadioDropdown.js 查看文件

label={props.firstOption.label} label={props.firstOption.label}
// number={item.numberOfProducts} // number={item.numberOfProducts}
fullWidth fullWidth
checked={!props.selected || props.selected._id === 0}
checked={
(props?.selected && !("_id" in props?.selected)) ||
props?.selected?._id === 0 || !props?.selected
}
onChange={props.setSelected} onChange={props.setSelected}
/> />
</DropdownItem> </DropdownItem>

+ 3
- 2
src/components/Cards/ItemDetailsCard/ImagesCarousel/ImagesCarousel.js 查看文件

<CloseButtonIcon /> <CloseButtonIcon />
</CloseButton> </CloseButton>
<Scroller isCarousel> <Scroller isCarousel>
{props?.offer?.offer?.images.map((image) => {
{props?.images.map((image) => {
if (!image) return; if (!image) return;
return ( return (
<OfferImage <OfferImage
})} })}
</Scroller> </Scroller>
<Offer> <Offer>
{t("carousel.offer")} <OfferSpan>{props.offer.offer.name}</OfferSpan>
{t("carousel.offer")} <OfferSpan>{props.offer.name}</OfferSpan>
</Offer> </Offer>
</ImagesCarouselContainer> </ImagesCarouselContainer>
</> </>
offer: PropTypes.any, offer: PropTypes.any,
onModalClose: PropTypes.any, onModalClose: PropTypes.any,
createOffer: PropTypes.bool, createOffer: PropTypes.bool,
images: PropTypes.array
}; };


export default ImagesCarousel; export default ImagesCarousel;

+ 22
- 24
src/components/Cards/ItemDetailsCard/ItemDetailsCard.js 查看文件

const offer = useMemo(() => { const offer = useMemo(() => {
if (props.offer) { if (props.offer) {
if ( if (
props.offer.offer._id === routeMatch.params?.offerId ||
props.createOffer
props.offer._id === routeMatch.params?.offerId &&
!props?.createOffer
) { ) {
return props.offer; return props.offer;
} }
if (props.createOffer) return props.offer.offer;
} }
return itemDetailsData; return itemDetailsData;
}, [props.offer, props.createOffer, routeMatch.params]); }, [props.offer, props.createOffer, routeMatch.params]);
}, []); }, []);


useEffect(() => { useEffect(() => {
if (offer?.offer?._id) {
increaseOfferCounter(offer?.offer?._id);
if (offer?._id) {
increaseOfferCounter(offer?._id);
} }
}, [offer]); }, [offer]);


const date = formatDateLocale(new Date(offer?.offer?._created));
const date = formatDateLocale(new Date(offer?._created));


const startExchange = () => { const startExchange = () => {
startChat(chats, offer?.offer, userId);
startChat(chats, offer, userId);
}; };


const showDeleteOfferModalHandler = () => { const showDeleteOfferModalHandler = () => {
dispatch( dispatch(
toggleDeleteOfferModal({ toggleDeleteOfferModal({
offer: offer.offer,
offer: offer,
isAdmin: props.isAdmin, isAdmin: props.isAdmin,
}) })
); );
dispatch( dispatch(
toggleEditOfferModal({ toggleEditOfferModal({
editOffer: true, editOffer: true,
offer: offer?.offer,
offer: offer,
isAdmin: props.isAdmin, isAdmin: props.isAdmin,
customUserId: offer?.offer?.userId,
customUserId: offer?.user?._id,
}) })
); );
}; };
const showPinOfferModalHandler = () => { const showPinOfferModalHandler = () => {
dispatch( dispatch(
toggleDeleteOfferModal({ toggleDeleteOfferModal({
offer: offer?.offer,
offer: offer,
pin: true, pin: true,
pinnedOffer: offer?.offer?.pinned,
pinnedOffer: offer?.pinned,
deleteOffer: false, deleteOffer: false,
}) })
); );
}; };


console.log(props)
return ( return (
<> <>
<ItemDetailsCardContainer <ItemDetailsCardContainer
<Info> <Info>
<Information <Information
icon={<CategoryIcon />} icon={<CategoryIcon />}
value={offer?.offer?.category?.name}
value={offer?.category?.name}
/> />
<Information <Information
icon={<SubcategoryIcon />} icon={<SubcategoryIcon />}
value={offer?.offer?.subcategory}
/>
<Information
icon={<QuantityIcon />}
value={offer?.offer?.condition}
value={offer?.subcategory}
/> />
<Information icon={<QuantityIcon />} value={offer?.condition} />
{!props.hideViews && ( {!props.hideViews && (
<Information
icon={<EyeIcon />}
value={offer?.offer?.views?.count}
/>
<Information icon={<EyeIcon />} value={offer?.views?.count} />
)} )}
</Info> </Info>
<PostDate previewCard={props.previewCard}>{date}</PostDate> <PostDate previewCard={props.previewCard}>{date}</PostDate>
<DateButtonsContainer> <DateButtonsContainer>
{props.isMyOffer && ( {props.isMyOffer && (
<ButtonsContainer> <ButtonsContainer>
<PinIconContainer onClick={showPinOfferModalHandler}>
{offer?.offer?.pinned ? <UnpinIcon /> : <PinIcon />}
</PinIconContainer>
{props?.isAdmin && (
<PinIconContainer onClick={showPinOfferModalHandler}>
{offer?.pinned ? <UnpinIcon /> : <PinIcon />}
</PinIconContainer>
)}
<EditIconContainer onClick={showEditOfferModalHandler}> <EditIconContainer onClick={showEditOfferModalHandler}>
<EditIcon /> <EditIcon />
</EditIconContainer> </EditIconContainer>

+ 19
- 21
src/components/Cards/ItemDetailsCard/OfferDetails/OfferDetails.js 查看文件

const [imagesCarouselModal, setImagesCarouselModal] = useState(false); const [imagesCarouselModal, setImagesCarouselModal] = useState(false);


useEffect(() => { useEffect(() => {
if (props?.offer?.offer?.images) {
props.offer.offer.images.map((file) => {
if (props?.offer?.images) {
props.offer.images.map((file) => {
if (file) { if (file) {
if (typeof file !== "string") { if (typeof file !== "string") {
var reader = new FileReader(); var reader = new FileReader();
} }
}); });
} }
}, [props?.offer?.offer?.images]);
const date = formatDateLocale(new Date(offer?.offer?._created));
return () => {
setImages([]);
};
}, [props?.offer?.images]);
const date = formatDateLocale(new Date(offer?._created));
const onModalClose = () => { const onModalClose = () => {
setImagesCarouselModal(false); setImagesCarouselModal(false);
}; };
console.log(props);
return ( return (
<> <>
<Details <Details
> >
{!isMobile && props.singleOffer && ( {!isMobile && props.singleOffer && (
<ScrollerVertical> <ScrollerVertical>
{props?.offer?.offer?.images.map((item, index) => (
{images.map((item, index) => (
<OfferImage <OfferImage
src={
props.createOffer
? images[index]
: getImageUrl(item, variants.offerCard, isMobile)
}
src={images[index]}
alt={t("offer.imageAlt")} alt={t("offer.imageAlt")}
key={item} key={item}
previewCard={props.previewCard} previewCard={props.previewCard}
singleOffer={props.singleOffer} singleOffer={props.singleOffer}
previewCard={props.previewCard} previewCard={props.previewCard}
> >
<OfferTitle singleOffer={props.singleOffer}>
{offer?.offer?.name}
</OfferTitle>
<OfferTitle singleOffer={props.singleOffer}>{offer?.name}</OfferTitle>
{isMobile && ( {isMobile && (
<ScrollerHorizontal> <ScrollerHorizontal>
{props?.offer?.offer?.images.map((item, index) => {
{images.map((item, index) => {
if (!item) return; if (!item) return;
return ( return (
<OfferImage <OfferImage
src={
props.createOffer
? images[index]
: getImageUrl(item, variants.offerCard, isMobile)
}
src={images[index]}
key={item} key={item}
previewCard={props.previewCard} previewCard={props.previewCard}
onClick={() => onClick={() =>
{t("itemDetailsCard.description")} {t("itemDetailsCard.description")}
</OfferDescriptionTitle> </OfferDescriptionTitle>
<OfferDescriptionText showBarterButton={props.showExchangeButton}> <OfferDescriptionText showBarterButton={props.showExchangeButton}>
{offer?.offer?.description}
{offer?.description}
</OfferDescriptionText> </OfferDescriptionText>
<DesciprtionPostDate previewCard={props.previewCard}> <DesciprtionPostDate previewCard={props.previewCard}>
{date} {date}
</OfferInfoContainer> </OfferInfoContainer>
</Details> </Details>
{imagesCarouselModal && ( {imagesCarouselModal && (
<ImagesCarousel offer={props.offer} onModalClose={onModalClose} />
<ImagesCarousel
offer={props.offer}
images={images}
onModalClose={onModalClose}
/>
)} )}
</> </>
); );

+ 19
- 0
src/components/Cards/LittleOfferCard/LittleOfferCard.styled.js 查看文件

display: flex; display: flex;
flex-direction: row; flex-direction: row;
position: relative; position: relative;
@media (max-width: 600px) {
width: 211px;
}
` `
export const OfferImage = styled.img` export const OfferImage = styled.img`
width: 54px; width: 54px;
` `
export const OfferDetails = styled(Box)` export const OfferDetails = styled(Box)`
display: flex; display: flex;
text-align: left;
flex-direction: column; flex-direction: column;
margin-top: 25px; margin-top: 25px;
margin-left: 9px; margin-left: 9px;
position: relative; position: relative;
top: 1.5px; top: 1.5px;
right: 2px; right: 2px;
& path {
stroke-width: 1;
}
` `
export const OfferSwapsIconContainer = styled(Icon)` export const OfferSwapsIconContainer = styled(Icon)`
width: 40px; width: 40px;
width: 40px; width: 40px;
height: 40px; height: 40px;
} }
@media (max-width: 600px) {
width: 32px;
height: 32px;
top: -15px;
right: -15px;
}
` `
export const OfferSwapsIcon = styled(Swaps)` export const OfferSwapsIcon = styled(Swaps)`
width: 18px; width: 18px;
height: 18px; height: 18px;
position: relative; position: relative;
top: 10px; top: 10px;
@media (max-width: 600px) {
width: 14px;
height: 14px;
top: 2px;
left: -4px;
}
` `

+ 5
- 1
src/components/Cards/MessageCard/MessageCard.styled.js 查看文件

overflow: hidden; overflow: hidden;
border-radius: 100%; border-radius: 100%;
@media (max-width: 600px) { @media (max-width: 600px) {
display: none;
width: 18px;
height: 18px;
min-width: 18px;
} }
`; `;
export const MessageContent = styled(Box)` export const MessageContent = styled(Box)`
min-width: 110px; min-width: 110px;
@media (max-width: 600px) { @media (max-width: 600px) {
width: 100%; width: 100%;
margin: 0;
${props => props.ismymessage ? "margin-right: 9px;" : "margin-left: 9px;"}
} }
`; `;
export const MessageText = styled(Typography)` export const MessageText = styled(Typography)`

+ 18
- 12
src/components/Cards/MiniChatCard/MiniChatCard.js 查看文件

import { replaceInRoute } from "../../../util/helpers/routeHelpers"; import { replaceInRoute } from "../../../util/helpers/routeHelpers";
import { DIRECT_CHAT_PAGE } from "../../../constants/pages"; import { DIRECT_CHAT_PAGE } from "../../../constants/pages";
import BlockedProfile from "../ProfileCard/BlockedProfile/BlockedProfile"; import BlockedProfile from "../ProfileCard/BlockedProfile/BlockedProfile";
import { useMemo } from "react";
import { useSelector } from "react-redux";
import { selectUserId } from "../../../store/selectors/loginSelectors";


const MiniChatCard = (props) => { const MiniChatCard = (props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { isMobile } = useIsMobile(); const { isMobile } = useIsMobile();
const userId = useSelector(selectUserId);
const changeChat = () => { const changeChat = () => {
history.push( history.push(
replaceInRoute(DIRECT_CHAT_PAGE, { replaceInRoute(DIRECT_CHAT_PAGE, {
chatId: props?.chat?.chat?._id,
chatId: props?.chat?._id,
}) })
); );
}; };
const interlocutorData = useMemo(() => {
if (props?.chat?.participants) {
let interlocutor = props?.chat?.participants[0]._id === userId ? 1 : 0;
return props?.chat?.participants[interlocutor];
}
return {};
}, [props?.chat]);
return ( return (
<MiniChatCardContainer selected={props.selected} onClick={changeChat}> <MiniChatCardContainer selected={props.selected} onClick={changeChat}>
<ProfileImage <ProfileImage
src={getImageUrl(
props?.chat?.interlocutorData?.image,
variants.chatCard,
isMobile
)}
src={getImageUrl(interlocutorData?.image, variants.chatCard, isMobile)}
/> />
<ProfileDetails isBlocked={props.chat.interlocutorData?._blocked}>
<ProfileDetails isBlocked={interlocutorData?._blocked}>
<ProfileNameContainer> <ProfileNameContainer>
<ProfileName selected={props.selected}> <ProfileName selected={props.selected}>
{props?.chat?.interlocutorData?.name}
{interlocutorData?.company?.name}
</ProfileName> </ProfileName>
{(props.chat.interlocutorData?._blocked ||
props.chat.interlocutorData?._deleted) && (
{(interlocutorData?._blocked || interlocutorData?._deleted) && (
<BlockedProfile <BlockedProfile
redText redText
chatCard chatCard
shortText shortText
aboveTitle aboveTitle
hideIcon hideIcon
deleted={props.chat.interlocutorData?._deleted}
deleted={interlocutorData?._deleted}
/> />
)} )}
</ProfileNameContainer> </ProfileNameContainer>
{t("messages.cardProduct")} {t("messages.cardProduct")}
</ProfileProduct> </ProfileProduct>
<ProfileProductName selected={props.selected}> <ProfileProductName selected={props.selected}>
{props?.chat?.offerData?.name}
{props?.chat?.offer?.name}
</ProfileProductName> </ProfileProductName>
</ProfileDetails> </ProfileDetails>
</MiniChatCardContainer> </MiniChatCardContainer>

+ 17
- 9
src/components/Cards/OfferCard/DeleteOffer/DeleteOffer.js 查看文件

import useIsMobile from "../../../../hooks/useIsMobile"; import useIsMobile from "../../../../hooks/useIsMobile";
import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter"; import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter";
import { selectQueryString } from "../../../../store/selectors/queryStringSelectors"; import { selectQueryString } from "../../../../store/selectors/queryStringSelectors";
import OfferDescription from "./OfferDescription/OfferDescription";
import CancelButton from "./CancelButton/CancelButton"; import CancelButton from "./CancelButton/CancelButton";
import SaveButton from "./SaveButton/SaveButton"; import SaveButton from "./SaveButton/SaveButton";
import { closeModal } from "../../../../store/actions/modal/modalActions"; import { closeModal } from "../../../../store/actions/modal/modalActions";
import { import {
dynamicRouteMatches, dynamicRouteMatches,
replaceInRoute,
routeMatches, routeMatches,
} from "../../../../util/helpers/routeHelpers"; } from "../../../../util/helpers/routeHelpers";
import { import {
ITEM_DETAILS_PAGE, ITEM_DETAILS_PAGE,
PROFILE_PAGE, PROFILE_PAGE,
} from "../../../../constants/pages"; } from "../../../../constants/pages";
import { OfferDescriptionContainer } from "./DeleteOfferLabeledCard/DeleteOfferLabeledCard.styled";


const DeleteOffer = (props) => { const DeleteOffer = (props) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const queryString = useSelector(selectQueryString); const queryString = useSelector(selectQueryString);
const history = useHistory(); const history = useHistory();
const userId = props.offer.userId;
const userId = props.offer.user._id;
const { isMobile } = useIsMobile(); const { isMobile } = useIsMobile();
const offerId = props.offer._id; const offerId = props.offer._id;
const closeDeleteModalHandler = () => { const closeDeleteModalHandler = () => {
dynamicRouteMatches(PROFILE_PAGE) || dynamicRouteMatches(PROFILE_PAGE) ||
dynamicRouteMatches(ADMIN_SINGLE_USER_PAGE) dynamicRouteMatches(ADMIN_SINGLE_USER_PAGE)
) )
dispatch(fetchProfileOffers(userId));
dispatch(fetchProfileOffers({ idProfile: userId, isAdmin: true }));
if (routeMatches(HOME_PAGE) || routeMatches(BASE_PAGE)) if (routeMatches(HOME_PAGE) || routeMatches(BASE_PAGE))
dispatch(fetchOffers({ queryString })); dispatch(fetchOffers({ queryString }));
if (
dynamicRouteMatches(ITEM_DETAILS_PAGE) ||
dynamicRouteMatches(ADMIN_ITEM_DETAILS_PAGE)
) {
if (dynamicRouteMatches(ITEM_DETAILS_PAGE)) {
history.push(
replaceInRoute(PROFILE_PAGE, {
profileId: userId,
})
);
}
if (dynamicRouteMatches(ADMIN_ITEM_DETAILS_PAGE)) {
if (props.pin) dispatch(fetchOneOffer(props.offer?._id)); if (props.pin) dispatch(fetchOneOffer(props.offer?._id));
else history.goBack();
else
history.push(
replaceInRoute(ADMIN_SINGLE_USER_PAGE, { profileId: userId })
);
} }
}; };


)} )}
/> />
</OfferImageContainer> </OfferImageContainer>
<OfferDescription
<OfferDescriptionContainer
offerName={props.offer.name} offerName={props.offer.name}
categoryName={props.offer.category.name} categoryName={props.offer.category.name}
/> />

+ 1
- 1
src/components/Cards/OfferCard/DeleteOffer/DeleteOffer.styled.js 查看文件

border-radius: 2px; border-radius: 2px;


@media screen and (max-width: 600px) { @media screen and (max-width: 600px) {
margin-right: 13px;
/* margin-right: 13px; */
} }
`; `;



+ 2
- 2
src/components/Cards/OfferCard/DeleteOffer/DeleteOfferLabeledCard/DeleteOfferLabeledCard.js 查看文件

import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { import {
OfferDescriptionContainer,
OfferImage, OfferImage,
OfferImageContainer, OfferImageContainer,
OfferInfo, OfferInfo,
getImageUrl, getImageUrl,
variants, variants,
} from "../../../../../util/helpers/imageUrlGetter"; } from "../../../../../util/helpers/imageUrlGetter";
import OfferDescription from "../OfferDescription/OfferDescription";
import useIsMobile from "../../../../../hooks/useIsMobile"; import useIsMobile from "../../../../../hooks/useIsMobile";


const DeleteOfferLabeledCard = (props) => { const DeleteOfferLabeledCard = (props) => {
)} )}
/> />
</OfferImageContainer> </OfferImageContainer>
<OfferDescription
<OfferDescriptionContainer
offerName={props.offer.name} offerName={props.offer.name}
categoryName={props.offer.category.name} categoryName={props.offer.category.name}
/> />

+ 5
- 1
src/components/Cards/OfferCard/DeleteOffer/DeleteOfferLabeledCard/DeleteOfferLabeledCard.styled.js 查看文件

import selectedTheme from "../../../../../themes"; import selectedTheme from "../../../../../themes";
import { ReactComponent as Remove } from "../../../../../assets/images/svg/trash-gold.svg"; import { ReactComponent as Remove } from "../../../../../assets/images/svg/trash-gold.svg";
import { IconButton } from "../../../../Buttons/IconButton/IconButton"; import { IconButton } from "../../../../Buttons/IconButton/IconButton";
import OfferDescription from "../OfferDescription/OfferDescription";
export const OfferInfo = styled(Box)` export const OfferInfo = styled(Box)`
width: 211px; width: 211px;
height: 90px; height: 90px;
border-radius: 2px; border-radius: 2px;


@media screen and (max-width: 600px) { @media screen and (max-width: 600px) {
margin-right: 13px;
/* margin-right: 13px; */
} }
`; `;


export const RemoveIcon = styled(Remove)` export const RemoveIcon = styled(Remove)`
cursor: default; cursor: default;
`; `;
export const OfferDescriptionContainer = styled(OfferDescription)`
margin-left: 0;
`

+ 8
- 4
src/components/Cards/OfferCard/DeleteOffer/OfferDescription/OfferDescription.js 查看文件

import { import {
CategoryIcon, CategoryIcon,
CategoryIconContainer, CategoryIconContainer,
OfferCategoryContainer,
OfferDescriptionCategory, OfferDescriptionCategory,
OfferDescriptionContainer, OfferDescriptionContainer,
OfferDescriptionTitle, OfferDescriptionTitle,


const OfferDescription = (props) => { const OfferDescription = (props) => {
return ( return (
<OfferDescriptionContainer>
<OfferDescriptionContainer className={props?.className}>
<OfferDescriptionTitle>{props.offerName}</OfferDescriptionTitle> <OfferDescriptionTitle>{props.offerName}</OfferDescriptionTitle>
<OfferDescriptionCategory>
<OfferCategoryContainer>
<CategoryIconContainer <CategoryIconContainer
color={selectedTheme.colors.iconStrokeDisabledColor} color={selectedTheme.colors.iconStrokeDisabledColor}
component="span" component="span"
> >
<CategoryIcon /> <CategoryIcon />
</CategoryIconContainer> </CategoryIconContainer>
{props.categoryName}
</OfferDescriptionCategory>
<OfferDescriptionCategory>
{props.categoryName}
</OfferDescriptionCategory>
</OfferCategoryContainer>
</OfferDescriptionContainer> </OfferDescriptionContainer>
); );
}; };
OfferDescription.propTypes = { OfferDescription.propTypes = {
offerName: PropTypes.string, offerName: PropTypes.string,
categoryName: PropTypes.string, categoryName: PropTypes.string,
className: PropTypes.string,
}; };


export default OfferDescription; export default OfferDescription;

+ 7
- 1
src/components/Cards/OfferCard/DeleteOffer/OfferDescription/OfferDescription.styled.js 查看文件

font-family: ${selectedTheme.fonts.textFont}; font-family: ${selectedTheme.fonts.textFont};
color: ${selectedTheme.colors.primaryDarkText}; color: ${selectedTheme.colors.primaryDarkText};
`; `;
export const OfferCategoryContainer = styled(Box)`
display: flex;
flex-direction: row;
white-space: nowrap;
`


export const CategoryIconContainer = styled(Icon)` export const CategoryIconContainer = styled(Icon)`
margin-right: 4px; margin-right: 4px;
position: relative; position: relative;
top: 2px; top: 2px;
display: inline;
& svg { & svg {
width: 14px; width: 14px;
position: relative; position: relative;
top: -6px;
top: -4px;
} }
`; `;
export const CategoryIcon = styled(Category)``; export const CategoryIcon = styled(Category)``;

+ 8
- 5
src/components/Cards/OfferCard/OfferCard.js 查看文件

}) })
); );
} else { } else {
history.push(
replaceInRoute(ITEM_DETAILS_PAGE, {
history.push({
pathname: replaceInRoute(ITEM_DETAILS_PAGE, {
offerId: itemId, offerId: itemId,
})
);
}),
state: {
view: true,
},
});
} }
} }
}; };
editOffer: true, editOffer: true,
offer: props.offer, offer: props.offer,
isAdmin: props.isAdmin, isAdmin: props.isAdmin,
customUserId: props?.offer?.userId,
customUserId: props?.offer?.user._id,
}) })
); );
}; };

+ 6
- 2
src/components/Cards/ProfileCard/ProfileControl/ProfileControl.js 查看文件

); );
}; };
const handleEditProfile = () => { const handleEditProfile = () => {
if (!props.profile?._blocked) {
console.log("edit")
console.log(props)
if (!props.profile?._blocked || props?.isAdmin) {
console.log("edit2")
dispatch( dispatch(
toggleEditProfileModal({ toggleEditProfileModal({
userId: props.profile._id, userId: props.profile._id,
); );
} }
}; };
console.log(props)
return ( return (
<ButtonsContainer> <ButtonsContainer>
{props.profile?._blocked && !props.isMobile && <BlockedProfile />}
{props.profile?._blocked && !props.isMobile && !props?.isAdmin && <BlockedProfile />}
{props.isAdmin && ( {props.isAdmin && (
<> <>
{props.profile?._blocked ? ( {props.profile?._blocked ? (

+ 6
- 6
src/components/Cards/RequestExchangeCard/RequestExchangeCard.js 查看文件

const requester = useSelector(selectRequester); const requester = useSelector(selectRequester);
const exchange = useSelector(selectExchange); const exchange = useSelector(selectExchange);
const amIBuyer = useMemo( const amIBuyer = useMemo(
() => exchange?.buyer?.userId === userId,
() => exchange?.buyer?.user?._id === userId,
[exchange, userId] [exchange, userId]
); );
const haveIAccepted = useMemo( const haveIAccepted = useMemo(
() => (amIBuyer ? exchange?.buyer?.accepted : exchange?.seller?.accepted), () => (amIBuyer ? exchange?.buyer?.accepted : exchange?.seller?.accepted),
[amIBuyer, exchange] [amIBuyer, exchange]
); );
const interlucatorUserId = useMemo(
() => (amIBuyer ? exchange?.seller?.userId : exchange?.buyer?.userId),
const interlocutorUserId = useMemo(
() => (amIBuyer ? exchange?.seller?.user?._id : exchange?.buyer?.user?._id),
[exchange, amIBuyer] [exchange, amIBuyer]
); );
const message = useMemo(() => { const message = useMemo(() => {
</MessageText> </MessageText>
); );
} }
} else if (requester === requesterStatus.INTERLUCATOR) {
} else if (requester === requesterStatus.interlocutor) {
return ( return (
<RequestExchangeMessage <RequestExchangeMessage
haveIAccepted={haveIAccepted} haveIAccepted={haveIAccepted}
chatId={props.chatId} chatId={props.chatId}
userId={userId} userId={userId}
interlucatorUserId={interlucatorUserId}
interlocutorUserId={interlocutorUserId}
/> />
); );
} }
return ""; return "";
}, [requester, t, haveIAccepted, interlucatorUserId, userId, exchange]);
}, [requester, t, haveIAccepted, interlocutorUserId, userId, exchange]);
return ( return (
<RequestExchangeCardContainer ismymessage={props.isMyMessage}> <RequestExchangeCardContainer ismymessage={props.isMyMessage}>
<InfoIcon /> <InfoIcon />

+ 2
- 0
src/components/Cards/RequestExchangeCard/RequestExchangeCard.styled.js 查看文件

min-width: 110px; min-width: 110px;
@media (max-width: 600px) { @media (max-width: 600px) {
width: 100%; width: 100%;
margin: 0;
${props => props.ismymessage ? "margin-right: 9px;" : "margin-left: 9px;"}
} }
`; `;
export const MessageText = styled(Typography)` export const MessageText = styled(Typography)`

+ 31
- 12
src/components/Cards/RequestExchangeCard/RequestExchangeMessage/RequestExchangeMessage.js 查看文件

import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { acceptExchangeSocket } from "../../../../socket/socket"; import { acceptExchangeSocket } from "../../../../socket/socket";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { selectExchange, selectRequester } from "../../../../store/selectors/exchangeSelector";
import { acceptExchange, setRequester } from "../../../../store/actions/exchange/exchangeActions";
import {
selectExchange,
selectRequester,
} from "../../../../store/selectors/exchangeSelector";
import {
acceptExchange,
setRequester,
} from "../../../../store/actions/exchange/exchangeActions";
import { addNewMessage } from "../../../../store/actions/chat/chatActions"; import { addNewMessage } from "../../../../store/actions/chat/chatActions";
import { convertLocalDateToUTCDate } from "../../../../util/helpers/dateHelpers"; import { convertLocalDateToUTCDate } from "../../../../util/helpers/dateHelpers";
import requesterStatus from "../../../../constants/requesterStatus"; import requesterStatus from "../../../../constants/requesterStatus";
import { selectJwtToken } from "../../../../store/selectors/loginSelectors";


const RequestExchangeMessage = (props) => { const RequestExchangeMessage = (props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useDispatch(); const dispatch = useDispatch();
const exchange = useSelector(selectExchange); const exchange = useSelector(selectExchange);
const requester = useSelector(selectRequester); const requester = useSelector(selectRequester);
const token = useSelector(selectJwtToken)


const handleAcceptExchange = () => {
const handleAcceptExchangeSuccess = () => {
console.log("accept salje i prima 3 POZVANA RESPONSE FUNKCIJA");
acceptExchangeSocket( acceptExchangeSocket(
props.chatId, props.chatId,
props.userId, props.userId,
props.interlucatorUserId,
props.interlocutorUserId,
token,
() => { () => {
dispatch(
acceptExchange({
exchangeId: exchange._id,
})
);
console.log("accept salje i prima 4 SOCKET FUNKCIJA");
dispatch( dispatch(
addNewMessage({ addNewMessage({
_id: props.chatId, _id: props.chatId,
message: { message: {
userId: props.userId,
user: {
_id: props.userId,
},
text: "", text: "",
isAcceptRequest: true, isAcceptRequest: true,
_created: convertLocalDateToUTCDate(new Date()), _created: convertLocalDateToUTCDate(new Date()),
} }
); );
}; };
const handleAcceptExchange = () => {
console.log("accept salje i prima 1 POZVANA FUNKCIJA");
dispatch(
acceptExchange({
exchangeId: exchange._id,
handleApiResponseSuccess: handleAcceptExchangeSuccess,
})
);
};
return ( return (
<RequestExchangeMessageContainer> <RequestExchangeMessageContainer>
<RequestExchangeMessageText> <RequestExchangeMessageText>
onClick={handleAcceptExchange} onClick={handleAcceptExchange}
disabled={props.haveIAccepted} disabled={props.haveIAccepted}
> >
{props.haveIAccepted ? t("messages.acceptedRequest") : t("messages.acceptRequest")}
{props.haveIAccepted
? t("messages.acceptedRequest")
: t("messages.acceptRequest")}
</RequestExchangeMessageButton> </RequestExchangeMessageButton>
</RequestExchangeMessageButtonsContainer> </RequestExchangeMessageButtonsContainer>
</RequestExchangeMessageContainer> </RequestExchangeMessageContainer>
children: PropTypes.node, children: PropTypes.node,
chatId: PropTypes.string, chatId: PropTypes.string,
userId: PropTypes.string, userId: PropTypes.string,
interlucatorUserId: PropTypes.string,
interlocutorUserId: PropTypes.string,
haveIAccepted: PropTypes.any, haveIAccepted: PropTypes.any,
}; };



+ 14
- 14
src/components/Cards/UserReviewsCard/UserReviewsCard.js 查看文件

...props.review, ...props.review,
}; };
} }
let userWhoGaveReview =
props?.review?.exchange?.buyer?.user?._id === props?.review?.user?._id
? "buyer"
: "seller";
let userWhoRevievedReview =
userWhoGaveReview === "buyer" ? "seller" : "buyer";
let isSuccessfulSwap = reviewEnum.YES.mainText.toUpperCase(); let isSuccessfulSwap = reviewEnum.YES.mainText.toUpperCase();
if ( if (
props.review.succeeded === reviewEnum.NO.backendText || props.review.succeeded === reviewEnum.NO.backendText ||
isGoodCommunication = reviewEnum.NO.mainText.toUpperCase(); isGoodCommunication = reviewEnum.NO.mainText.toUpperCase();
return { return {
_id: props.review._id, _id: props.review._id,
name:
props.review?.reviewAdditionalData?.userWhoGave?.company?.name ||
props.review?.offer?.name,
image:
props.review?.reviewAdditionalData?.userWhoGave?.image ||
props.review?.offer?.image,
userId: props.review.userId,
name: props?.review?.exchange[userWhoGaveReview]?.user?.company?.name,
image: props?.review?.exchange[userWhoGaveReview]?.user?.image,
userId: props.review.user?._id,
isGoodCommunication, isGoodCommunication,
isSuccessfulSwap, isSuccessfulSwap,
quote: props?.review?.message, quote: props?.review?.message,
offerName:
props?.review?.reviewAdditionalData?.offerData?.name ||
props?.review?.userWhoGaveReview?.name,
offerImage:
props?.review?.reviewAdditionalData?.offerData?.firstImage ||
props?.review?.userWhoGaveReview?.image,
offerName: props?.review?.exchange?.offer?.name,
offerImage: props?.review?.exchange?.offer?.images[0],
userWhoReceived: userWhoReceived:
props?.review?.reviewAdditionalData?.userWhoReceived?.company?.name,
props?.review?.exchange[userWhoRevievedReview]?.user?.company?.name,
_deleted: props?.review?._deleted, _deleted: props?.review?._deleted,
}; };
}, [props.review]); }, [props.review]);
handleRemove={handleRemove} handleRemove={handleRemove}
hasGivenReview={props.hasGivenReview} hasGivenReview={props.hasGivenReview}
rightReviews={props.rightReviews} rightReviews={props.rightReviews}
isProfileReviews={props?.isProfileReviews}
isAdmin={props.isAdmin} isAdmin={props.isAdmin}
/> />
); );
hasGivenReview: PropTypes.bool, hasGivenReview: PropTypes.bool,
rightReviews: PropTypes.bool, rightReviews: PropTypes.bool,
isAdmin: PropTypes.bool, isAdmin: PropTypes.bool,
userId: PropTypes.string,
}; };
UserReviewsCard.defaultProps = { UserReviewsCard.defaultProps = {
isProfileReviews: false, isProfileReviews: false,

+ 3
- 0
src/components/Cards/UserReviewsCard/UserReviewsSingleCard/RemoveButton/RemoveButton.js 查看文件

import { RemoveButtonContainer, RemoveIcon } from "./RemoveButton.styled"; import { RemoveButtonContainer, RemoveIcon } from "./RemoveButton.styled";


const RemoveButton = (props) => { const RemoveButton = (props) => {
console.log(props)
return ( return (
<RemoveButtonContainer <RemoveButtonContainer
isRemoved={props.isRemoved} isRemoved={props.isRemoved}
onClick={props.onClick} onClick={props.onClick}
hasGivenReview={props.hasGivenReview} hasGivenReview={props.hasGivenReview}
isProfileReviews={props?.isProfileReviews}
> >
<RemoveIcon isRemoved={props.isRemoved} /> <RemoveIcon isRemoved={props.isRemoved} />
</RemoveButtonContainer> </RemoveButtonContainer>
onClick: PropTypes.func, onClick: PropTypes.func,
hasGivenReview: PropTypes.bool, hasGivenReview: PropTypes.bool,
isRemoved: PropTypes.bool, isRemoved: PropTypes.bool,
isProfileReviews: PropTypes.any
}; };


export default RemoveButton; export default RemoveButton;

+ 15
- 9
src/components/Cards/UserReviewsCard/UserReviewsSingleCard/RemoveButton/RemoveButton.styled.js 查看文件



export const RemoveButtonContainer = styled(IconButton)` export const RemoveButtonContainer = styled(IconButton)`
position: absolute; position: absolute;
top: ${(props) => (props.hasGivenReview ? "79px" : "16px")};
top: ${(props) =>
props.hasGivenReview && props.isProfileReviews ? "79px" : "16px"};
right: 16px; right: 16px;
background-color: ${props => props.isRemoved ? "transparent" : selectedTheme.colors.primaryIconBackgroundColor};
background-color: ${(props) =>
props.isRemoved
? "transparent"
: selectedTheme.colors.primaryIconBackgroundColor};
border-radius: 100%; border-radius: 100%;
width: 32px; width: 32px;
height: 32px; height: 32px;
${props => props.isRemoved && css`
& button:hover {
background-color: transparent;
cursor: auto;
}
`}
${(props) =>
props.isRemoved &&
css`
& button:hover {
background-color: transparent;
cursor: auto;
}
`}
& button { & button {
width: 32px; width: 32px;
height: 32px; height: 32px;
`; `;
export const RemoveIcon = styled(Remove)` export const RemoveIcon = styled(Remove)`
& path { & path {
stroke: ${props => props.isRemoved && selectedTheme.colors.blockedColor};
stroke: ${(props) => props.isRemoved && selectedTheme.colors.blockedColor};
} }
`; `;

+ 3
- 1
src/components/Cards/UserReviewsCard/UserReviewsSingleCard/ReviewQuote/ReviewQuote.styled.js 查看文件

export const ThumbContainer = styled(Grid)` export const ThumbContainer = styled(Grid)`
max-width: 20px; max-width: 20px;
`; `;
export const ReviewQuoteTextContainer = styled(Grid)``;
export const ReviewQuoteTextContainer = styled(Box)`
flex: 1;
`;
export const ReviewQuoteText = styled(Typography)` export const ReviewQuoteText = styled(Typography)`
font-family: ${selectedTheme.fonts.textFont}; font-family: ${selectedTheme.fonts.textFont};
font-size: 16px; font-size: 16px;

+ 2
- 0
src/components/Cards/UserReviewsCard/UserReviewsSingleCard/UserReviewsSingleCard.js 查看文件

<RemoveButton <RemoveButton
review={props.review} review={props.review}
onClick={handleRemove} onClick={handleRemove}
isProfileReviews={props?.isProfileReviews}
hasGivenReview={props.hasGivenReview} hasGivenReview={props.hasGivenReview}
isRemoved={props.review?._deleted} isRemoved={props.review?._deleted}
/> />
isAdmin: PropTypes.bool, isAdmin: PropTypes.bool,
className: PropTypes.string, className: PropTypes.string,
lastReview: PropTypes.bool, lastReview: PropTypes.bool,
isProfileReviews: PropTypes.any,
}; };
UserReviewsSingleCard.defaultProps = { UserReviewsSingleCard.defaultProps = {
lastReview: false, lastReview: false,

+ 3
- 2
src/components/ChatColumn/ChatColumn.js 查看文件

fetchChats, fetchChats,
} from "../../store/actions/chat/chatActions"; } from "../../store/actions/chat/chatActions";
import useSorting from "../../hooks/useOffers/useSorting"; import useSorting from "../../hooks/useOffers/useSorting";
import { addMesageListener, removeMessageListener } from "../../socket/socket";
import { addMessageListener, removeMessageListener } from "../../socket/socket";
import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors"; import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors";
import { CHAT_SCOPE } from "../../store/actions/chat/chatActionConstants"; import { CHAT_SCOPE } from "../../store/actions/chat/chatActionConstants";
import SkeletonChatColumn from "./SkeletonChatColumn/SkeletonChatColumn"; import SkeletonChatColumn from "./SkeletonChatColumn/SkeletonChatColumn";
history.goBack(); history.goBack();
return; return;
} }
addMesageListener(({ succeed, data }) => {
addMessageListener(({ succeed, data }) => {

if (succeed) { if (succeed) {
dispatch( dispatch(
addNewMessage({ addNewMessage({

+ 4
- 8
src/components/CreateReview/CreateReview.js 查看文件

import FirstStepCreateReview from "./FirstStep/FirstStepCreateReview"; import FirstStepCreateReview from "./FirstStep/FirstStepCreateReview";
import SecondStepCreateReview from "./SecondStep/SecondStepCreateReview"; import SecondStepCreateReview from "./SecondStep/SecondStepCreateReview";
import ThirdStepCreateReview from "./ThirdStep/ThirdStepCreateReview"; import ThirdStepCreateReview from "./ThirdStep/ThirdStepCreateReview";
import { useDispatch, useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { giveReview } from "../../store/actions/review/reviewActions"; import { giveReview } from "../../store/actions/review/reviewActions";
import { selectUserId } from "../../store/selectors/loginSelectors";
import { reviewEnum } from "../../enums/reviewEnum"; import { reviewEnum } from "../../enums/reviewEnum";
import { fetchExchange } from "../../store/actions/exchange/exchangeActions"; import { fetchExchange } from "../../store/actions/exchange/exchangeActions";
import { closeModal } from "../../store/actions/modal/modalActions"; import { closeModal } from "../../store/actions/modal/modalActions";
const [informations, setInformations] = useState({}); const [informations, setInformations] = useState({});
const [currentStep, setCurrentStep] = useState(1); const [currentStep, setCurrentStep] = useState(1);
const dispatch = useDispatch(); const dispatch = useDispatch();
const userId = useSelector(selectUserId);
const closeModalHandler = () => { const closeModalHandler = () => {
dispatch(closeModal()); dispatch(closeModal());
}; };
setTimeout(() => {
dispatch(closeModal());
}, 3000);
const handleApiResponseSuccess = () => { const handleApiResponseSuccess = () => {
dispatch(fetchExchange(props.exchange._id)); dispatch(fetchExchange(props.exchange._id));
}; };
dispatch( dispatch(
giveReview({ giveReview({
review: { review: {
exchangeId: props.exchange._id,
userId: userId,
exchange: {
_id: props.exchange._id,
},
succeeded, succeeded,
communication, communication,
message: informations.comment, message: informations.comment,

+ 15
- 7
src/components/CreateReview/CreateReview.styled.js 查看文件

max-width: 100vw; max-width: 100vw;
left: 0; left: 0;
top: 0; top: 0;
padding: 0 30px;
padding: 0 18px;
} }
`; `;
export const NextButton = styled(PrimaryButton)` export const NextButton = styled(PrimaryButton)`
@media (max-width: 600px) { @media (max-width: 600px) {
height: 42px; height: 42px;
position: absolute; position: absolute;
bottom: 15px;
width: calc(100% - 48px);
bottom: 9px;
width: calc(100vw - 36px);
left: 0; left: 0;
margin-left: 24px;
margin-left: 9px;
} }
`; `;
export const CloseButton = styled(IconButton)` export const CloseButton = styled(IconButton)`
top: 36px; top: 36px;
right: 36px; right: 36px;
@media (max-width: 600px) { @media (max-width: 600px) {
top: 24px;
right: 24px;
top: 32px;
right: 22px;
} }
`; `;
export const CloseIcon = styled(Close)` export const CloseIcon = styled(Close)`
left: 36px; left: 36px;


@media (max-width: 600px) { @media (max-width: 600px) {
top: 24px;
top: 40px;
left: 24px; left: 24px;
width: 18px; width: 18px;
height: 18px; height: 18px;
margin-bottom: 24px; margin-bottom: 24px;
} }
`; `;
export const CreateReviewFlexContainer = styled(Box)`
@media (max-width: 600px) {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
`

+ 2
- 1
src/components/CreateReview/FirstStep/FirstStepCreateReview.js 查看文件

const FirstStepCreateReview = (props) => { const FirstStepCreateReview = (props) => {
const offer = props.offer; const offer = props.offer;
const interlocutor = props.interlocutor; const interlocutor = props.interlocutor;
console.log(props);
const { t } = useTranslation(); const { t } = useTranslation();
const handleSubmit = (values) => { const handleSubmit = (values) => {
props.goToNextStep(values); props.goToNextStep(values);
)} )}
/> />
</ProfileImageContainer> </ProfileImageContainer>
<ProfileName>{interlocutor.name}</ProfileName>
<ProfileName>{interlocutor.company.name}</ProfileName>
<LittleOfferCard <LittleOfferCard
image={offer?.images[0]} image={offer?.images[0]}
name={offer?.name} name={offer?.name}

+ 5
- 3
src/components/CreateReview/FirstStep/FirstStepCreateReview.styled.js 查看文件

text-align: center; text-align: center;
padding: 36px; padding: 36px;
@media (max-width: 600px) { @media (max-width: 600px) {
padding: 18px;
padding: 0;
padding-top: 36px;
flex: 1;
} }
`; `;
export const CreateReviewTitle = styled(Typography)` export const CreateReviewTitle = styled(Typography)`
text-align: left; text-align: left;
${props => props.exchange && `background-color: ${selectedTheme.colors.backgroundSponsoredColor};`} ${props => props.exchange && `background-color: ${selectedTheme.colors.backgroundSponsoredColor};`}
@media (max-width: 600px) { @media (max-width: 600px) {
height: 33px;
height: 40px;
font-size: 14px; font-size: 14px;
margin-bottom: 12px;
margin-bottom: 18px;
} }
`; `;
export const SelectOption = styled(Option)` export const SelectOption = styled(Option)`

+ 13
- 13
src/components/CreateReview/SecondStep/SecondStepCreateReview.js 查看文件

: t("reviews.modalTitle")} : t("reviews.modalTitle")}
</CreateReviewTitle> </CreateReviewTitle>
<ReviewCard <ReviewCard
givingReview
profileReviews={[
{
name: mineProfile?.company?.name,
image: mineProfile?.image,
offerName: props?.offer?.name,
offerImage: props?.offer?.images[0],
isGoodCommunication: props.review?.correctCommunication,
isSuccessfulSwap: props.review?.exchangeSucceed,
quote: props.review.comment,
},
]}
givingReview={{
name: mineProfile?.company?.name,
image: mineProfile?.image,
offerName: props?.offer?.name,
offerImage: props?.offer?.images[0],
isGoodCommunication: props.review?.correctCommunication,
isSuccessfulSwap: props.review?.exchangeSucceed,
quote: props.review.comment,
}}
/>
<NextButton
removingReview={props.removingReview}
onClick={goToNextStep}
/> />
<NextButton removingReview={props.removingReview} onClick={goToNextStep} />
</SecondStepCreateReviewContainer> </SecondStepCreateReviewContainer>
); );
}; };

+ 13
- 1
src/components/CreateReview/SecondStep/SecondStepCreateReview.styled.js 查看文件

export const SecondStepCreateReviewContainer = styled(Box)` export const SecondStepCreateReviewContainer = styled(Box)`
padding: 36px; padding: 36px;
@media (max-width: 600px) { @media (max-width: 600px) {
padding: 18px;
padding: 0;
padding-top: 36px;
} }
`; `;
export const ReviewCard = styled(UserReviews)` export const ReviewCard = styled(UserReviews)`
margin: 0; margin: 0;
} }
} }
& * {
overflow:hidden;
}
@media (max-width: 600px) {
position: relative;
top: -24px;
& > div {
position: static;
margin-top: 0px;
}
}
`; `;

+ 6
- 0
src/components/CreateReview/ThirdStep/ThirdStepCreateReview.js 查看文件

ThirdStepCreateReviewContainer, ThirdStepCreateReviewContainer,
} from "./ThirdStepCreateReview.styled"; } from "./ThirdStepCreateReview.styled";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useDispatch } from "react-redux";
import { closeModal } from "../../../store/actions/modal/modalActions";


const ThirdStepCreateReview = () => { const ThirdStepCreateReview = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useDispatch();
setTimeout(() => {
dispatch(closeModal());
}, 3000);
return ( return (
<ThirdStepCreateReviewContainer> <ThirdStepCreateReviewContainer>
<LogoImage /> <LogoImage />

+ 48
- 43
src/components/DirectChat/DirectChat.js 查看文件

import SkeletonDirectChat from "./SkeletonDirectChat/SkeletonDirectChat"; import SkeletonDirectChat from "./SkeletonDirectChat/SkeletonDirectChat";
import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors"; import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors";
import { CHAT_SCOPE } from "../../store/actions/chat/chatActionConstants"; import { CHAT_SCOPE } from "../../store/actions/chat/chatActionConstants";
import { selectUserId } from "../../store/selectors/loginSelectors";
import {
selectJwtToken,
selectUserId,
} from "../../store/selectors/loginSelectors";
import { import {
acceptExchangeSocket, acceptExchangeSocket,
addMesageListener,
addMessageListener,
removeMessageListener, removeMessageListener,
} from "../../socket/socket"; } from "../../socket/socket";
import { makeErrorToastMessage } from "../../store/utils/makeToastMessage"; import { makeErrorToastMessage } from "../../store/utils/makeToastMessage";
} from "../../store/selectors/exchangeSelector"; } from "../../store/selectors/exchangeSelector";
import { import {
acceptExchange, acceptExchange,
fetchExchange,
setRequester, setRequester,
} from "../../store/actions/exchange/exchangeActions"; } from "../../store/actions/exchange/exchangeActions";
import { convertLocalDateToUTCDate } from "../../util/helpers/dateHelpers"; import { convertLocalDateToUTCDate } from "../../util/helpers/dateHelpers";
import requesterStatus from "../../constants/requesterStatus"; import requesterStatus from "../../constants/requesterStatus";
import exchangeStatus from "../../constants/exchangeStatus"; import exchangeStatus from "../../constants/exchangeStatus";
import { NEW_CHAT } from "../../constants/chatConstants";


const DirectChat = () => { const DirectChat = () => {
const chat = useSelector(selectSelectedChat); const chat = useSelector(selectSelectedChat);
const location = useLocation(); const location = useLocation();
const dispatch = useDispatch(); const dispatch = useDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const token = useSelector(selectJwtToken);
const exchange = useSelector(selectExchange); const exchange = useSelector(selectExchange);
const requester = useSelector(selectRequester); const requester = useSelector(selectRequester);




const offerObject = useMemo(() => { const offerObject = useMemo(() => {
if (location?.state?.offerId) { if (location?.state?.offerId) {
return offer?.offer;
return offer;
} }
return chat?.offer?.offer;
return chat?.offer;
}, [chat, location.state, offer]); }, [chat, location.state, offer]);


const chatObject = useMemo(() => { const chatObject = useMemo(() => {
}, [chat, location.state]); }, [chat, location.state]);


const amIBuyer = useMemo( const amIBuyer = useMemo(
() => exchange.buyer?.userId === userId,
() => exchange.buyer?.user?._id === userId,
[exchange, userId] [exchange, userId]
); );


let haveIAccepted = amIBuyer let haveIAccepted = amIBuyer
? exchange.buyer?.accepted ? exchange.buyer?.accepted
: exchange.seller?.accepted; : exchange.seller?.accepted;
let haveInterlucatorAccepted = amIBuyer
let haveinterlocutorAccepted = amIBuyer
? exchange.seller?.accepted ? exchange.seller?.accepted
: exchange.buyer?.accepted; : exchange.buyer?.accepted;
let haveIReviewed = amIBuyer let haveIReviewed = amIBuyer
? exchange.buyer.givenReview ? exchange.buyer.givenReview
: exchange.seller.givenReview; : exchange.seller.givenReview;
if (haveIAccepted) { if (haveIAccepted) {
if (haveInterlucatorAccepted) {
if (haveinterlocutorAccepted) {
if (haveIReviewed) { if (haveIReviewed) {
return exchangeStatus.REVIEWED; return exchangeStatus.REVIEWED;
} else { } else {
return exchangeStatus.I_OFFERED; return exchangeStatus.I_OFFERED;
} }
} else { } else {
if (haveInterlucatorAccepted) {
if (haveinterlocutorAccepted) {
return exchangeStatus.I_AM_OFFERED; return exchangeStatus.I_AM_OFFERED;
} else { } else {
return exchangeStatus.INITIAL; return exchangeStatus.INITIAL;


const interlocutorObject = useMemo(() => { const interlocutorObject = useMemo(() => {
if (location?.state?.offerId) { if (location?.state?.offerId) {
return {
image: offer?.companyData?.image,
name: offer?.companyData?.company?.name,
location: offer?.companyData?.company?.contacts?.location,
userId: offer?.offer?.userId,
telephone: offer?.companyData?.company?.contacts?.telephone,
};
return offer?.user;
}
if (chat?.participants) {
let interlocutor = userId === chat?.participants[0]._id ? 1 : 0;
return chat?.participants[interlocutor];
} }
return {
...chat?.interlocutor,
userId:
chat?.chat?.participants[0] === userId
? chat?.chat?.participants[1]
: chat?.chat?.participants[0],
};
return {};
}, [chat, location.state, offer]); }, [chat, location.state, offer]);


// Fetch chat after it is created // Fetch chat after it is created


// Listener to socket.IO chat // Listener to socket.IO chat
useEffect(() => { useEffect(() => {
addMesageListener(({ succeed, data }) => {
addMessageListener(({ succeed, data }) => {
if (succeed) { if (succeed) {
if ( if (
[...allChats].find((item) => { [...allChats].find((item) => {
return item.chat._id === data.chatId;
return item._id === data.chatId;
}) })
) { ) {
dispatch( dispatch(
message: data.message, message: data.message,
}) })
); );
if (
data.message?.isAcceptRequest &&
requester === requesterStatus.NOONE
) {
dispatch(setRequester(requesterStatus.INTERLUCATOR));
if (data.message?.isAcceptRequest) {
dispatch(fetchExchange(exchange?._id));
if (requester === requesterStatus.NOONE) {
dispatch(setRequester(requesterStatus.interlocutor));
}
} }
} else { } else {
dispatch(fetchChats()); dispatch(fetchChats());
} }
}); });
return () => removeMessageListener(); return () => removeMessageListener();
}, [allChats, routeMatch]);

}, [allChats, routeMatch, requester]);


const refreshChat = () => { const refreshChat = () => {
if (routeMatch.params?.chatId === "newMessage") {
if (routeMatch.params?.chatId === NEW_CHAT) {
dispatch(fetchOneOffer(location.state.offerId)); dispatch(fetchOneOffer(location.state.offerId));
dispatch(setOneChat({})); dispatch(setOneChat({}));
} else { } else {
dispatch(fetchOneChat(routeMatch.params?.chatId)); dispatch(fetchOneChat(routeMatch.params?.chatId));
} }
}; };
const handleAcceptExchange = () => {
const handleAcceptExchangeSuccess = () => {
let interlocutor = userId === chat?.participants[0]._id ? 1 : 0;
acceptExchangeSocket( acceptExchangeSocket(
chat?.chat?._id,
chat?._id,
userId, userId,
chat?.interlocutor?._id,
chat?.participants[interlocutor]._id,
token,
() => { () => {
dispatch(
acceptExchange({
exchangeId: exchange._id,
})
);
dispatch( dispatch(
addNewMessage({ addNewMessage({
_id: chat?.chat?._id,
_id: chat?._id,
message: { message: {
userId,
user: {
_id: userId,
},
isAcceptRequest: true, isAcceptRequest: true,
text: "", text: "",
_created: convertLocalDateToUTCDate(new Date()), _created: convertLocalDateToUTCDate(new Date()),
} }
); );
}; };
const handleAcceptExchange = () => {
console.log("accept salje i prima 1 POZVANA FUNKCIJA");
dispatch(
acceptExchange({
exchangeId: exchange._id,
handleApiResponseSuccess: handleAcceptExchangeSuccess,
})
);
};
return ( return (
<DirectChatContainer> <DirectChatContainer>
{isLoadingDirectChat || isLoadingDirectChat === undefined ? ( {isLoadingDirectChat || isLoadingDirectChat === undefined ? (
<DirectChatContent <DirectChatContent
chat={chatObject} chat={chatObject}
exchangeState={exchangeState} exchangeState={exchangeState}
interlucator={interlocutorObject}
interlocutor={interlocutorObject}
refreshChat={refreshChat} refreshChat={refreshChat}
/> />
</DirectChatContainer> </DirectChatContainer>

+ 14
- 10
src/components/DirectChat/DirectChatContent/DirectChatContent.js 查看文件

const myProfileImage = useSelector(selectMineProfilePicture); const myProfileImage = useSelector(selectMineProfilePicture);
const messagesRef = useRef(null); const messagesRef = useRef(null);
const requester = useSelector(selectRequester); const requester = useSelector(selectRequester);
const interlucatorProfileImage = props?.interlucator?.image;
const interlocutorProfileImage = props?.interlocutor?.image;
const isLoadingChatContent = useSelector( const isLoadingChatContent = useSelector(
selectIsLoadingByActionType(CHAT_SCOPE) selectIsLoadingByActionType(CHAT_SCOPE)
); );
const messages = props?.chat?.chat?.messages;
const messages = props?.chat?.messages;
useEffect(() => { useEffect(() => {
messagesRef.current?.scrollTo({ messagesRef.current?.scrollTo({
top: messagesRef.current.scrollHeight, top: messagesRef.current.scrollHeight,
behaviour: "smooth", behaviour: "smooth",
}); });
window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" }); window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" });
}, [props?.chat?.chat?.messages, messagesRef, isLoadingChatContent]);
}, [props?.chat?.messages, messagesRef, isLoadingChatContent]);


const handleRefresh = () => { const handleRefresh = () => {
props.refreshChat(); props.refreshChat();
) : ( ) : (
<DirectChatContentContainer> <DirectChatContentContainer>
<DirectChatContentHeader <DirectChatContentHeader
interlucator={props?.interlucator}
interlocutor={props?.interlocutor}
exchangeState={props.exchangeState} exchangeState={props.exchangeState}
/> />
<MessagesList ref={messagesRef} exchangeState={props?.exchangeState}> <MessagesList ref={messagesRef} exchangeState={props?.exchangeState}>
{messages?.map((item) => { {messages?.map((item) => {
const isMyMessage = userId === item.userId;
const isMyMessage = userId === item.user?._id;
const image = isMyMessage const image = isMyMessage
? myProfileImage ? myProfileImage
: interlucatorProfileImage;
if (requester === requesterStatus.INTERLUCATOR && isMyMessage && item?.isAcceptRequest)
: interlocutorProfileImage;
if (
requester === requesterStatus.interlocutor &&
isMyMessage &&
item?.isAcceptRequest
)
return; return;
return ( return (
<MessageContainer key={item?._id || item?._created}> <MessageContainer key={item?._id || item?._created}>
<RequestExchangeCard <RequestExchangeCard
isMyMessage={isMyMessage} isMyMessage={isMyMessage}
message={item} message={item}
chatId={props?.chat?.chat?._id}
chatId={props?.chat?._id}
/> />
) : ( ) : (
<MessageCard <MessageCard
<DirectChatNewMessage <DirectChatNewMessage
chat={props?.chat} chat={props?.chat}
refreshChat={handleRefresh} refreshChat={handleRefresh}
interlucator={props.interlucator}
interlocutor={props.interlocutor}
/> />
</DirectChatContentContainer> </DirectChatContentContainer>
)} )}
DirectChatContent.propTypes = { DirectChatContent.propTypes = {
children: PropTypes.node, children: PropTypes.node,
chat: PropTypes.any, chat: PropTypes.any,
interlucator: PropTypes.any,
interlocutor: PropTypes.any,
refreshChat: PropTypes.func, refreshChat: PropTypes.func,
exchangeState: PropTypes.any, exchangeState: PropTypes.any,
}; };

+ 1
- 1
src/components/DirectChat/DirectChatContent/DirectChatContent.styled.js 查看文件

/* justify-content: flex-end; */ /* justify-content: flex-end; */
/* align-items: flex-end; */ /* align-items: flex-end; */
@media (max-width: 600px) { @media (max-width: 600px) {
padding: 18px 0;
padding: 18px 18px;
} }
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 5px; width: 5px;

+ 19
- 12
src/components/DirectChat/DirectChatContent/DirectChatContentHeader/DirectChatContentHeader.js 查看文件

const [phonePopoverAnchorEl, setPhonePopoverAnchorEl] = useState(null); const [phonePopoverAnchorEl, setPhonePopoverAnchorEl] = useState(null);
const { isMobile } = useIsMobile(); const { isMobile } = useIsMobile();
const mineProfileBlocked = useSelector(selectAmIBlocked); const mineProfileBlocked = useSelector(selectAmIBlocked);

const togglePhonePopover = (event) => { const togglePhonePopover = (event) => {
if (props.interlucator?.telephone) {
if (props.interlocutor?.company?.contacts?.telephone) {
setShowPhonePopover((prevState) => !prevState); setShowPhonePopover((prevState) => !prevState);
setPhonePopoverAnchorEl((prevState) => { setPhonePopoverAnchorEl((prevState) => {
if (prevState) return null; if (prevState) return null;
} }
}; };
const routeToUser = () => { const routeToUser = () => {
if (!props?.interlucator?._blocked)
if (!props?.interlocutor?._blocked)
history.push( history.push(
replaceInRoute(PROFILE_PAGE, { replaceInRoute(PROFILE_PAGE, {
profileId: props?.interlucator?.userId,
profileId: props?.interlocutor?._id,
}) })
); );
}; };
<ProfileImage <ProfileImage
onClick={routeToUser} onClick={routeToUser}
src={getImageUrl( src={getImageUrl(
props?.interlucator?.image,
props?.interlocutor?.image,
variants.chatHeader, variants.chatHeader,
isMobile isMobile
)} )}
/> />
<ProfileDetails> <ProfileDetails>
<ProfileName onClick={routeToUser}> <ProfileName onClick={routeToUser}>
{props?.interlucator?.name}
{props?.interlocutor?.company?.name}
</ProfileName> </ProfileName>
<ProfileLocation> <ProfileLocation>
<ProfileLocationIcon /> <ProfileLocationIcon />
<ProfileLocationText> <ProfileLocationText>
{props?.interlucator?.location}
{props?.interlocutor?.company?.contacts?.location}
</ProfileLocationText> </ProfileLocationText>
</ProfileLocation> </ProfileLocation>
</ProfileDetails> </ProfileDetails>
<PhoneIconContainer <PhoneIconContainer
disabled={ disabled={
mineProfileBlocked || mineProfileBlocked ||
props?.interlucator?._blocked ||
!props.interlucator?.telephone
props?.interlocutor?._blocked ||
!props.interlocutor?.company?.contacts?.telephone
} }
onClick={togglePhonePopover} onClick={togglePhonePopover}
> >
open={showPhonePopover} open={showPhonePopover}
anchorRight anchorRight
onClose={togglePhonePopover} onClose={togglePhonePopover}
content={<PhonePopover phoneNumber={props.interlucator?.telephone} />}
content={
<PhonePopover
phoneNumber={props.interlocutor?.company?.contacts?.telephone}
/>
}
/> />
</DirectChatContentHeaderContainer> </DirectChatContentHeaderContainer>
{(props.exchangeState === exchangeStatus.I_OFFERED || {(props.exchangeState === exchangeStatus.I_OFFERED ||
<DirectChatHeaderStatusContainer> <DirectChatHeaderStatusContainer>
<DirectChatHeaderStatusText> <DirectChatHeaderStatusText>
{props.exchangeState === exchangeStatus.I_OFFERED {props.exchangeState === exchangeStatus.I_OFFERED
? t("messages.requestSentLong")
? isMobile
? t("messages.requestSentShort")
: t("messages.requestSentLong")
: isMobile
? t("messages.requestSuccessfulShort")
: t("messages.requestSuccessfulLong")} : t("messages.requestSuccessfulLong")}
</DirectChatHeaderStatusText> </DirectChatHeaderStatusText>
</DirectChatHeaderStatusContainer> </DirectChatHeaderStatusContainer>


DirectChatContentHeader.propTypes = { DirectChatContentHeader.propTypes = {
children: PropTypes.node, children: PropTypes.node,
interlucator: PropTypes.any,
interlocutor: PropTypes.any,
exchangeState: PropTypes.bool, exchangeState: PropTypes.bool,
}; };



+ 7
- 0
src/components/DirectChat/DirectChatContent/DirectChatContentHeader/DirectChatContentHeader.styled.js 查看文件

height: 39px; height: 39px;
width: 100%; width: 100%;
padding: 9px 36px; padding: 9px 36px;
@media (max-width: 600px) {
height: 36px;
padding: 8px 18px;
}
`; `;
export const DirectChatHeaderStatusText = styled(Typography)` export const DirectChatHeaderStatusText = styled(Typography)`
font-family: ${selectedTheme.fonts.textFont}; font-family: ${selectedTheme.fonts.textFont};
font-size: 16px; font-size: 16px;
line-height: 21px; line-height: 21px;
color: white; color: white;
@media (max-width: 600px) {
font-size: 14px;
}
`; `;

+ 28
- 11
src/components/DirectChat/DirectChatHeader/DirectChatHeader.js 查看文件

import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { selectExchange } from "../../../store/selectors/exchangeSelector"; import { selectExchange } from "../../../store/selectors/exchangeSelector";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { fetchExchange } from "../../../store/actions/exchange/exchangeActions";
import {
fetchExchange,
setExchange,
} from "../../../store/actions/exchange/exchangeActions";
import { selectSelectedChat } from "../../../store/selectors/chatSelectors"; import { selectSelectedChat } from "../../../store/selectors/chatSelectors";
import { selectUserId } from "../../../store/selectors/loginSelectors"; import { selectUserId } from "../../../store/selectors/loginSelectors";
import { toggleCreateReviewModal } from "../../../store/actions/modal/modalActions"; import { toggleCreateReviewModal } from "../../../store/actions/modal/modalActions";
import { setOneChat } from "../../../store/actions/chat/chatActions";


const DirectChatHeader = (props) => { const DirectChatHeader = (props) => {
const exchange = useSelector(selectExchange); const exchange = useSelector(selectExchange);
const chat = useSelector(selectSelectedChat); const chat = useSelector(selectSelectedChat);


useEffect(() => { useEffect(() => {
if (chat?.chat?.exchangeId) refetchExchange();
}, [chat]);
return () => {
dispatch(setExchange({}));
dispatch(setOneChat({}));
};
}, []);

useEffect(() => {
if (chat?.exchange?._id) refetchExchange();
}, [chat?.exchange]);


const isDisabledReviews = useMemo(() => { const isDisabledReviews = useMemo(() => {
if (!exchange.valid) return true; if (!exchange.valid) return true;
if (exchange.seller?.userId === userId && exchange.seller?.givenReview)
if (exchange.seller?.user._id === userId && exchange.seller?.givenReview)
return true; return true;
if (exchange.buyer?.userId === userId && exchange.buyer?.givenReview)
if (exchange.buyer?.user._id === userId && exchange.buyer?.givenReview)
return true; return true;
if (chat?.offer?._deleted) return true;
if (props.interlocutor?._blocked) return true;
return false;
}, [exchange, userId, props.interlocutor, chat]);

const isDisabledCheckButton = useMemo(() => {
if (props?.interlocutor?._blocked) return true;
if (chat?.offer?._deleted) return true;
return false; return false;
}, [exchange, userId]);
}, [props.interlocutor, chat]);


const showReviewModal = () => { const showReviewModal = () => {
dispatch( dispatch(
}; };


const refetchExchange = () => { const refetchExchange = () => {
dispatch(fetchExchange(chat.chat.exchangeId));
dispatch(fetchExchange(chat.exchange?._id));
}; };


const acceptExchange = () => { const acceptExchange = () => {
<OfferCard <OfferCard
offer={props.offer} offer={props.offer}
aboveChat aboveChat
disabledReviews={props.interlocutor?._blocked || isDisabledReviews}
disabledReviews={isDisabledReviews}
makeReview={showReviewModal} makeReview={showReviewModal}
acceptExchange={acceptExchange} acceptExchange={acceptExchange}
exchangeState={props?.exchangeState} exchangeState={props?.exchangeState}
dontShowViews dontShowViews
disabledCheckButton={
props.interlocutor?._blocked || props?.offer?._deleted
}
disabledCheckButton={isDisabledCheckButton}
/> />
</DirectChatHeaderContainer> </DirectChatHeaderContainer>
); );

+ 20
- 15
src/components/DirectChat/DirectChatNewMessage/DirectChatNewMessage.js 查看文件

import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { sendMessage } from "../../../socket/socket"; import { sendMessage } from "../../../socket/socket";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { selectUserId } from "../../../store/selectors/loginSelectors";
import { selectJwtToken, selectUserId } from "../../../store/selectors/loginSelectors";
import { import {
addNewMessage, addNewMessage,
startNewChat, startNewChat,
const { t } = useTranslation(); const { t } = useTranslation();
const location = useLocation(); const location = useLocation();
const history = useHistory(); const history = useHistory();
const token = useSelector(selectJwtToken)
const userId = useSelector(selectUserId); const userId = useSelector(selectUserId);
const handleSend = useCallback( const handleSend = useCallback(
(newChatId = undefined) => { (newChatId = undefined) => {
if (props.chat?.chat?._id) {
const chatId = props.chat?.chat?._id || newChatId;
sendMessage(chatId, userId, typedValue, props.interlucator.userId);
if (typedValue?.length === 0) return;
if (props?.chat?._id) {
const chatId = props.chat?._id || newChatId;
sendMessage(chatId, userId, typedValue, props.interlocutor._id, token);
dispatch( dispatch(
addNewMessage({ addNewMessage({
_id: chatId, _id: chatId,
message: { message: {
userId,
user: {
_id: userId,
},
text: typedValue, text: typedValue,
_created: convertLocalDateToUTCDate(new Date()), _created: convertLocalDateToUTCDate(new Date()),
}, },
}) })
); );
if (props.chat?.chat?._id) {
if (!exchange.valid && exchange.seller.userId === userId) {
if (props.chat?._id) {
if (!exchange.valid && exchange.seller.user._id === userId) {
dispatch(validateExchange(exchange._id)); dispatch(validateExchange(exchange._id));
} }
} }
} }
setTypedValue(""); setTypedValue("");
}, },
[typedValue, props.chat?.chat?._id, userId, props.interlucator.userId]
[typedValue, props.chat?._id, userId, props.interlocutor]
); );
const handleMessageSendSuccess = (newChatId) => { const handleMessageSendSuccess = (newChatId) => {
history.replace(`${newChatId}`); history.replace(`${newChatId}`);
startNewChat({ startNewChat({
offerId, offerId,
message: typedValue, message: typedValue,
interlucatorUserId: props.interlucator.userId,
interlocutorUserId: props.interlocutor._id,
handleMessageSendSuccess, handleMessageSendSuccess,
}) })
); );
}; };
console.log(props)
if (mineProfileBlocked) { if (mineProfileBlocked) {
return <NotAllowedChat mineProfileBlocked />; return <NotAllowedChat mineProfileBlocked />;
} }
if (props?.chat?.interlocutor?._deleted) {
if (props?.interlocutor?._deleted) {
return <NotAllowedChat deleted />; return <NotAllowedChat deleted />;
} }
if (props?.chat?.offer?.offer?._deleted) {
if (props?.chat?.offer?._deleted) {
return <NotAllowedChat />; return <NotAllowedChat />;
} }
if (props?.chat?.interlocutor?._blocked) {
return <NotAllowedChat blocked />;
}
// if (props?.interlocutor?._blocked) {
// return <NotAllowedChat blocked />;
// }
return ( return (
<> <>
<DirectChatNewMessageContainer> <DirectChatNewMessageContainer>
children: PropTypes.node, children: PropTypes.node,
chatId: PropTypes.any, chatId: PropTypes.any,
refreshChat: PropTypes.func, refreshChat: PropTypes.func,
interlucator: PropTypes.any,
interlocutor: PropTypes.any,
chat: PropTypes.any, chat: PropTypes.any,
}; };



+ 11
- 7
src/components/DirectChat/MiniChatColumn/MiniChatColumn.js 查看文件

const newChat = useMemo(() => { const newChat = useMemo(() => {
if (location.state?.offerId) { if (location.state?.offerId) {
return { return {
interlocutorData: {
image: offer?.companyData?.image,
name: offer?.companyData?.company?.name,
},
offerData: {
name: offer?.offer?.name,
participants: [
{
image: offer?.user?.image,
company: {
name: offer?.user?.company?.name,
},
},
],
offer: {
name: offer?.name,
}, },
}; };
} }
<MiniChatCard <MiniChatCard
key={Date.now() * Math.random()} key={Date.now() * Math.random()}
chat={item} chat={item}
selected={item?.chat?._id === selectedChat?.chat?._id}
selected={item?._id === selectedChat?._id}
/> />
); );
})} })}

+ 6
- 2
src/components/Footer/AboutFooter.styled.js 查看文件

position: relative; position: relative;
top: 61px; top: 61px;


@media (max-width: 1194px) {
text-align: left;
}

@media (max-width: 600px) { @media (max-width: 600px) {
font-size: 14px; font-size: 14px;
top: 30px; top: 30px;
position: relative; position: relative;
top: 8px; top: 8px;


@media (max-width: 600px) {
@media (max-width: 700px) {
display: none; display: none;
} }
`; `;


export const Arrow = styled(ArrowButton)` export const Arrow = styled(ArrowButton)`
transform: rotate(-90deg);
transform: rotate(-45deg);
`; `;

+ 4
- 0
src/components/Header/AboutHeader/AboutHeader.styled.js 查看文件

flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
gap: 36px; gap: 36px;

@media (min-width: 900px) and (max-width: 1200px) {
margin-right: -250px;
}
`; `;
export const LinkRoute = styled(Link)` export const LinkRoute = styled(Link)`
text-decoration: none; text-decoration: none;

+ 7
- 1
src/components/Header/Drawer/Buttons/MyProfileButton/MyProfileButton.js 查看文件

import { selectProfileName } from "../../../../../store/selectors/profileSelectors"; import { selectProfileName } from "../../../../../store/selectors/profileSelectors";
import { selectUserId } from "../../../../../store/selectors/loginSelectors"; import { selectUserId } from "../../../../../store/selectors/loginSelectors";
import history from "../../../../../store/utils/history"; import history from "../../../../../store/utils/history";
import { PROFILE_PAGE } from "../../../../../constants/pages";
import { replaceInRoute } from "../../../../../util/helpers/routeHelpers";


const MyProfileButton = (props) => { const MyProfileButton = (props) => {
const name = useSelector(selectProfileName); const name = useSelector(selectProfileName);
const { t } = useTranslation(); const { t } = useTranslation();
const handleClick = () => { const handleClick = () => {
props.toggleDrawer(); props.toggleDrawer();
history.push(`/profile/${userId}`);
history.push(
replaceInRoute(PROFILE_PAGE, {
profileId: userId,
})
);
}; };
return ( return (
<DrawerButton onClick={handleClick}> <DrawerButton onClick={handleClick}>

+ 20
- 3
src/components/Header/Header.js 查看文件

AuthButtonsContainer, AuthButtonsContainer,
HeaderContainer, HeaderContainer,
LogoContainer, LogoContainer,
MarketplaceLinkRoute,
MarketplaceLinkRouteContainer,
ToolsButtonsContainer, ToolsButtonsContainer,
ToolsContainer, ToolsContainer,
} from "./Header.styled"; } from "./Header.styled";
import RegisterButton from "./RegisterButton/RegisterButton"; import RegisterButton from "./RegisterButton/RegisterButton";
import useIsMobile from "../../hooks/useIsMobile"; import useIsMobile from "../../hooks/useIsMobile";
import { toggleCreateOfferModal } from "../../store/actions/modal/modalActions"; import { toggleCreateOfferModal } from "../../store/actions/modal/modalActions";
import { ReactComponent as Marketplace } from "../../assets/images/svg/package.svg";
import { useTranslation } from "react-i18next";


const Header = () => { const Header = () => {
const theme = useTheme(); const theme = useTheme();
const [shouldShow, setShouldShow] = useState(true); const [shouldShow, setShouldShow] = useState(true);
const routeMatch = useRouteMatch(); const routeMatch = useRouteMatch();
const { isMobile } = useIsMobile(); const { isMobile } = useIsMobile();
const { t } = useTranslation();


// Dont show header on auth routes(login, register, etc.) and admin routes // Dont show header on auth routes(login, register, etc.) and admin routes
useEffect(() => { useEffect(() => {
logo: true, logo: true,
}, },
}); });
searchRef.current.value = "";
if (searchRef?.current) searchRef.current.value = "";
} }
}; };


if (!shouldShow) { if (!shouldShow) {
return <></>; return <></>;
} }
console.log(history);


return ( return (
<HeaderContainer> <HeaderContainer>
/> />
) : ( ) : (
<React.Fragment> <React.Fragment>
<LoginButton />
<RegisterButton />
{routeMatches(ABOUT_PAGE) ? (
<MarketplaceLinkRouteContainer>
<MarketplaceLinkRoute onClick={() => handleLogoClick()}>
{t("admin.navigation.marketplace")}
</MarketplaceLinkRoute>
<Marketplace />
</MarketplaceLinkRouteContainer>
) : (
<>
<LoginButton />
<RegisterButton />
</>
)}
</React.Fragment> </React.Fragment>
)} )}
</AuthButtonsContainer> </AuthButtonsContainer>

+ 29
- 0
src/components/Header/Header.styled.js 查看文件

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


export const DrawerContainer = styled(Box)` export const DrawerContainer = styled(Box)`
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
`; `;
export const HeaderContainer = styled(Box)``; export const HeaderContainer = styled(Box)``;

export const MarketplaceLinkRouteContainer = styled(Box)`
display: flex;

@media (max-width: 1200px) {
position: relative;
right: -200px;
}
`;

export const MarketplaceLinkRoute = styled(Link)`
text-decoration: none;
font-family: ${selectedTheme.fonts.textFont};
font-size: 16px;
line-height: 22px;
margin-right: 10px;
letter-spacing: 0.02em;
margin-left: 70px;
border-bottom: 1px dashed ${selectedTheme.colors.primaryPurple};
&:hover {
border-bottom: 1px solid ${selectedTheme.colors.iconYellowColor};
}

@media (max-width: 600px) {
display: none;
}
`;

+ 2
- 0
src/components/Header/SearchInput/SearchInput.js 查看文件

const { t } = useTranslation(); const { t } = useTranslation();
const handleSearch = () => { const handleSearch = () => {
if (routeMatches(HOME_PAGE) || routeMatches(BASE_PAGE)) { if (routeMatches(HOME_PAGE) || routeMatches(BASE_PAGE)) {
console.log("uslo unutra")
debounceHelper(() => props.handleSearch(ref.current.value), 500); debounceHelper(() => props.handleSearch(ref.current.value), 500);
} }
}; };
console.log(routeMatches(HOME_PAGE))
const handleManualSearch = () => { const handleManualSearch = () => {
debounceHelper(() => {}, 500); debounceHelper(() => {}, 500);
props.handleSearch(ref.current.value); props.handleSearch(ref.current.value);

+ 12
- 6
src/components/ItemDetails/ItemDetails.js 查看文件

selectIsLoadingByActionType(ONE_OFFER_SCOPE) selectIsLoadingByActionType(ONE_OFFER_SCOPE)
); );
let isMyProfile = useMemo(() => { let isMyProfile = useMemo(() => {
if (
offer?.offer?.userId?.toString() === userId?.toString() ||
props.isAdmin
)
if (offer?.user?._id?.toString() === userId?.toString() || props.isAdmin)
return true; return true;
return false; return false;
}, [offer, userId, props.isAdmin]); }, [offer, userId, props.isAdmin]);
) : ( ) : (
<> <>
{!props.singleOffer && ( {!props.singleOffer && (
<ItemDetailsHeaderCard offer={offer} isMyProfile={isMyProfile} />
<ItemDetailsHeaderCard
offer={offer}
isMyProfile={isMyProfile}
isAdmin={props?.isAdmin}
/>
)} )}
{props.singleOffer && ( {props.singleOffer && (
<OfferIconContainer> <OfferIconContainer>
<OfferIconText>{t("offer.product")}</OfferIconText> <OfferIconText>{t("offer.product")}</OfferIconText>
</OfferIconContainer> </OfferIconContainer>
)} )}
<ItemDetailsCard offer={offer} isMyOffer={isMyProfile} isAdmin={props.isAdmin} singleOffer />
<ItemDetailsCard
offer={offer}
isMyOffer={isMyProfile}
isAdmin={props.isAdmin}
singleOffer
/>
</> </>
)} )}
</ItemDetailsContainer> </ItemDetailsContainer>

+ 36
- 11
src/components/ItemDetails/ItemDetailsHeaderCard/ItemDetailsHeaderCard.js 查看文件

import itemDetailsData from "../../../notFoundData/itemDetailsData"; import itemDetailsData from "../../../notFoundData/itemDetailsData";
import { Tooltip } from "@mui/material"; import { Tooltip } from "@mui/material";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { replaceInRoute } from "../../../util/helpers/routeHelpers";
import {
ADMIN_SINGLE_USER_PAGE,
DIRECT_CHAT_PAGE,
PROFILE_PAGE,
} from "../../../constants/pages";
import { NEW_CHAT } from "../../../constants/chatConstants";


const ItemDetailsHeaderCard = (props) => { const ItemDetailsHeaderCard = (props) => {
const history = useHistory(); const history = useHistory();
const { t } = useTranslation(); const { t } = useTranslation();
const offer = useMemo(() => { const offer = useMemo(() => {
if (props.offer) { if (props.offer) {
if (props.offer.offer._id === routeMatch.params.offerId) {
if (props.offer._id === routeMatch.params.offerId) {
return props.offer; return props.offer;
} }
} }
const { isMobile } = useIsMobile(); const { isMobile } = useIsMobile();


const handleGoProfile = () => { const handleGoProfile = () => {
history.push(`/profile/${offer?.offer?.userId}`);
if (props?.isAdmin) {
history.push(
replaceInRoute(ADMIN_SINGLE_USER_PAGE, {
profileId: props?.offer?.user?._id,
})
);
} else {
history.push(
replaceInRoute(PROFILE_PAGE, {
profileId: props?.offer?.user?._id,
})
);
}
}; };
const messageUser = (offer) => { const messageUser = (offer) => {
const chatItem = chats.find(
(item) => item.chat.offerId === offer?.offer?._id
);
const chatItem = chats.find((item) => item.offer._id === offer?._id);
if (chatItem !== undefined) { if (chatItem !== undefined) {
history.push(`/messages/${chatItem.chat._id}`);
history.push(DIRECT_CHAT_PAGE, {
chatId: chatItem._id,
});
} else { } else {
if (offer?.offer?.userId !== userId) {
history.push(`/messages/newMessage`, {
offerId: offer?.offer?._id,
if (offer?.user?._id !== userId) {
history.push({
pathname: replaceInRoute(DIRECT_CHAT_PAGE, {
chatId: NEW_CHAT,
}),
state: {
offerId: offer?._id,
},
}); });
} }
} }
<HeaderTop> <HeaderTop>
<OfferImage <OfferImage
src={getImageUrl( src={getImageUrl(
offer?.companyData?.image ? offer.companyData.image : "",
offer?.user?.image ? offer.user.image : "",
variants.profileImage, variants.profileImage,
isMobile isMobile
)} )}
/> />
<OfferDetails> <OfferDetails>
<OfferTitle isMyProfile={props.isMyProfile} onClick={handleGoProfile}> <OfferTitle isMyProfile={props.isMyProfile} onClick={handleGoProfile}>
{offer?.companyData?.company?.name}
{offer?.user?.company?.name}
</OfferTitle> </OfferTitle>
<PIBDetail offer={offer} isMyProfile={props.isMyProfile} /> <PIBDetail offer={offer} isMyProfile={props.isMyProfile} />
<CategoryDetail offer={offer} isMyProfile={props.isMyProfile} /> <CategoryDetail offer={offer} isMyProfile={props.isMyProfile} />
offer: PropTypes.any, offer: PropTypes.any,
isMyProfile: PropTypes.bool, isMyProfile: PropTypes.bool,
singleOffer: PropTypes.bool, singleOffer: PropTypes.bool,
isAdmin: PropTypes.bool,
}; };
ItemDetailsHeaderCard.defaultProps = { ItemDetailsHeaderCard.defaultProps = {
halfwidth: false, halfwidth: false,

+ 1
- 1
src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/Category/CategoryDetail.js 查看文件

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

+ 1
- 1
src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/PIB/PIBDetail.js 查看文件

<PIB /> <PIB />
</PIBIcon> </PIBIcon>
<DetailText isMyProfile={props.isMyProfile}> <DetailText isMyProfile={props.isMyProfile}>
{`${t("itemDetailsCard.PIB")}${offer?.companyData?.company?.PIB}`}
{`${t("itemDetailsCard.PIB")}${offer?.user?.company?.PIB}`}
</DetailText> </DetailText>
</DetailContainer> </DetailContainer>
); );

+ 5
- 5
src/components/ItemDetails/ItemDetailsHeaderCard/StatisticDetails/StatisticDetails.js 查看文件

const { t } = useTranslation(); const { t } = useTranslation();
const offer = props.offer; const offer = props.offer;
const percentOfSucceededExchanges = useMemo(() => { const percentOfSucceededExchanges = useMemo(() => {
if (offer?.companyData?.statistics?.exchanges?.succeeded === 0) {
if (offer?.user?.statistics?.exchanges?.succeeded === 0) {
return 0 + "%"; return 0 + "%";
} else { } else {
return ( return (
Math.ceil( Math.ceil(
(offer?.companyData?.statistics?.exchanges?.total /
offer?.companyData?.statistics?.exchanges?.succeeded) *
(offer?.user?.statistics?.exchanges?.total /
offer?.user?.statistics?.exchanges?.succeeded) *
100 100
) + "%" ) + "%"
); );
<BottomDetails> <BottomDetails>
<StatusText> <StatusText>
<StatusValue> <StatusValue>
{offer?.companyData?.statistics?.publishes?.count}
{offer?.user?.statistics?.publishes?.count}
</StatusValue> </StatusValue>
{t("itemDetailsCard.offers")} {t("itemDetailsCard.offers")}
</StatusText> </StatusText>
<StatusText> <StatusText>
<StatusValue> <StatusValue>
{offer?.companyData?.statistics?.views?.count}
{offer?.user?.statistics?.views?.count}
</StatusValue> </StatusValue>
{t("itemDetailsCard.totalViews")} {t("itemDetailsCard.totalViews")}
</StatusText> </StatusText>

+ 8
- 5
src/components/MarketPlace/Header/Header.js 查看文件

headerTitle={headerTitle} headerTitle={headerTitle}
headerIcon={headerIcon} headerIcon={headerIcon}
offers={props?.offers} offers={props?.offers}
myOffers={props?.myOffers}
hideBackButton={props?.hideBackButton} hideBackButton={props?.hideBackButton}
/> />
{/* ^^^^^^ */} {/* ^^^^^^ */}
/> />


{/* Select option to choose sorting */} {/* Select option to choose sorting */}
<HeaderSelect
myOffers={props?.myOffers}
sorting={sorting}
hideSorting={props?.hideSorting}
/>
{!props?.hideSorting && (
<HeaderSelect
myOffers={props?.myOffers}
sorting={sorting}
hideSorting={props?.hideSorting}
/>
)}
{/* ^^^^^^ */} {/* ^^^^^^ */}
</HeaderOptions> </HeaderOptions>
</HeaderContainer> </HeaderContainer>

+ 3
- 0
src/components/MarketPlace/Header/TooltipHeader/TooltipHeader.js 查看文件

const handleClickCategory = () => { const handleClickCategory = () => {
props?.offers?.filters?.locations.clear(); props?.offers?.filters?.locations.clear();
props?.offers?.filters?.subcategory.clear(); props?.offers?.filters?.subcategory.clear();
props?.offers?.filters?.companies?.clear();
props?.offers?.applyFilters(); props?.offers?.applyFilters();
}; };
const handleClickSubcategory = () => { const handleClickSubcategory = () => {
props?.offers?.filters?.locations.clear(); props?.offers?.filters?.locations.clear();
props?.offers?.filters?.companies?.clear();
props?.offers?.applyFilters(); props?.offers?.applyFilters();
}; };
const goBack = () => { const goBack = () => {
history.goBack(); history.goBack();
}; };
console.log(props)
return ( return (
<Tooltip title={headerString.text}> <Tooltip title={headerString.text}>
<TooltipInnerContainer> <TooltipInnerContainer>

+ 10
- 2
src/components/MarketPlace/Offers/Offers.js 查看文件

const Offers = (props) => { const Offers = (props) => {
const offers = props?.offers; const offers = props?.offers;
const arrayForMapping = Array.apply(null, Array(4)).map(() => {}); const arrayForMapping = Array.apply(null, Array(4)).map(() => {});
console.log("rerender");

return ( return (
<> <>
<OffersFilterButton />
<OffersFilterButton
offers={props?.offers}
isAdmin={props?.isAdmin}
myOffers={props?.myOffers}
toggleFilters={props?.toggleFilters}
/>
<OffersSearchField /> <OffersSearchField />
<OffersNotFound />
{!props?.skeleton && <OffersNotFound />}
<OffersList <OffersList
loading={props?.skeleton} loading={props?.skeleton}
offers={offers} offers={offers}
isGrid={props?.isGrid} isGrid={props?.isGrid}
isUsers={props?.isUsers} isUsers={props?.isUsers}
users={props?.users} users={props?.users}
myOffers={props?.myOffers}
/> />
{props?.skeleton && {props?.skeleton &&
arrayForMapping.map((item, index) => ( arrayForMapping.map((item, index) => (

+ 6
- 2
src/components/MarketPlace/Offers/OffersList/OffersList.js 查看文件

import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { useSelector } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { selectTotalOffers } from "../../../../store/selectors/offersSelectors"; import { selectTotalOffers } from "../../../../store/selectors/offersSelectors";
import { OffersContainer } from "./OffersList.styled"; import { OffersContainer } from "./OffersList.styled";
import BigProfileCard from "../../../Cards/ProfileCard/BigProfileCard/BigProfileCard"; import BigProfileCard from "../../../Cards/ProfileCard/BigProfileCard/BigProfileCard";
import { startChat } from "../../../../util/helpers/chatHelper"; import { startChat } from "../../../../util/helpers/chatHelper";
import { selectLatestChats } from "../../../../store/selectors/chatSelectors"; import { selectLatestChats } from "../../../../store/selectors/chatSelectors";
import { selectUserId } from "../../../../store/selectors/loginSelectors"; import { selectUserId } from "../../../../store/selectors/loginSelectors";
import { setRequester } from "../../../../store/actions/exchange/exchangeActions";
import requesterStatus from "../../../../constants/requesterStatus";


const OffersList = (props) => { const OffersList = (props) => {
const totalOffers = useSelector(selectTotalOffers); const totalOffers = useSelector(selectTotalOffers);
const chats = useSelector(selectLatestChats); const chats = useSelector(selectLatestChats);
const userId = useSelector(selectUserId); const userId = useSelector(selectUserId);
const dispatch = useDispatch();
const offers = props?.offers; const offers = props?.offers;
const messageOneUser = (offer) => { const messageOneUser = (offer) => {
dispatch(setRequester(requesterStatus.NOONE));
startChat(chats, offer, userId); startChat(chats, offer, userId);
}; };
return ( return (
offer={item} offer={item}
halfwidth={props?.isGrid} halfwidth={props?.isGrid}
messageUser={messageOneUser} messageUser={messageOneUser}
isMyOffer={item?.userId === userId || props?.isAdmin}
isMyOffer={item?.user._id === userId || props?.isAdmin}
isAdmin={props?.isAdmin} isAdmin={props?.isAdmin}
/> />
); );

+ 1
- 1
src/components/Modals/EditCategory/EditCategory.js 查看文件

}; };


const handleApiResponseSuccess = () => { const handleApiResponseSuccess = () => {
if (clickedOnNext) {
if (clickedOnNext && props?.showSecondButton) {
formik.resetForm(); formik.resetForm();
inputRef.current.focus(); inputRef.current.focus();
} else closeModalHandler(); } else closeModalHandler();

+ 23
- 9
src/components/Popovers/MyMessages/MyMessages.js 查看文件

import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { makeErrorToastMessage } from "../../../store/utils/makeToastMessage"; import { makeErrorToastMessage } from "../../../store/utils/makeToastMessage";
import { EyeIcon } from "./MyMessages.styled"; import { EyeIcon } from "./MyMessages.styled";
import { DIRECT_CHAT_PAGE } from "../../../constants/pages";
import { replaceInRoute } from "../../../util/helpers/routeHelpers";


export const MyMessages = (props) => { export const MyMessages = (props) => {
const { t } = useTranslation(); const { t } = useTranslation();


const convertMessages = (messages) => { const convertMessages = (messages) => {
return messages return messages
.map((item) => ({
src: item.interlocutorData.image,
title: item.interlocutorData.name,
onClick: () => goToMessage(item?.chat?._id),
text: "Proizvod: ",
bigText: item.offerData.name,
}))
.map((item) => {
let interlocutor = userId === item.participants[0]._id ? 1 : 0;
return {
src: item.participants[interlocutor]?.image,
title: item.participants[interlocutor]?.company?.name,
onClick: () => goToMessage(item?._id),
text: "Proizvod: ",
bigText: item.offer.name,
};
})
.slice(0, 2); .slice(0, 2);
}; };


}, [chats]); }, [chats]);
const goToMessages = () => { const goToMessages = () => {
if (lastChats.length !== 0) { if (lastChats.length !== 0) {
history.push(`/messages/${chats[0].chat?._id}`);
console.log(chats);
history.push({
pathname: replaceInRoute(DIRECT_CHAT_PAGE, {
chatId: chats[0]._id,
}),
});
props.closePopover(); props.closePopover();
} else { } else {
makeErrorToastMessage(t("messages.noMessagesToast")); makeErrorToastMessage(t("messages.noMessagesToast"));
} }
}; };
const goToMessage = (chatId) => { const goToMessage = (chatId) => {
history.push(`/messages/${chatId}`);
history.push(
replaceInRoute(DIRECT_CHAT_PAGE, {
chatId,
})
);
props.closePopover(); props.closePopover();
}; };
return ( return (

+ 7
- 2
src/components/Popovers/MyPosts/MyPosts.js 查看文件

import { fetchMineHeaderOffers } from "../../../store/actions/offers/offersActions"; import { fetchMineHeaderOffers } from "../../../store/actions/offers/offersActions";
import { selectProfileName } from "../../../store/selectors/profileSelectors"; import { selectProfileName } from "../../../store/selectors/profileSelectors";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { MY_OFFERS_PAGE } from "../../../constants/pages";
import { ITEM_DETAILS_PAGE, MY_OFFERS_PAGE } from "../../../constants/pages";
import { useMemo } from "react"; import { useMemo } from "react";
import { replaceInRoute } from "../../../util/helpers/routeHelpers";


export const MyPosts = (props) => { export const MyPosts = (props) => {
const { t } = useTranslation(); const { t } = useTranslation();
}, [arrayOfMineOffers, mineOffers]); }, [arrayOfMineOffers, mineOffers]);


const goToOffer = (id) => { const goToOffer = (id) => {
history.push(`/proizvodi/${id}`);
history.push(
replaceInRoute(ITEM_DETAILS_PAGE, {
offerId: id,
})
);
props.closePopover(); props.closePopover();
}; };
const goToMySwaps = () => { const goToMySwaps = () => {

+ 37
- 2
src/components/Prices/Plan/Plan.styled.js 查看文件

${(props) => ${(props) =>
props.highlighted && `box-shadow: 4px 4px 9px rgba(0, 0, 0, 0.12);`} props.highlighted && `box-shadow: 4px 4px 9px rgba(0, 0, 0, 0.12);`}


@media (min-width: 1194px) and (max-width: 1430px) {
width: 346px;
height: 100%;
min-width: 346px;
}

@media (max-width: 600px) { @media (max-width: 600px) {
min-width: 303px;
width: 100%;
width: 267px;
height: fit-content;
padding: 36px 18px;
margin-top: 0;
}

@media (max-width: 428px) {
min-width: 100%;
height: fit-content; height: fit-content;
padding: 36px 18px; padding: 36px 18px;
margin-top: 0; margin-top: 0;
props.highlighted props.highlighted
? selectedTheme.colors.primaryYellow ? selectedTheme.colors.primaryYellow
: selectedTheme.colors.primaryPurple}; : selectedTheme.colors.primaryPurple};

@media (min-width: 1194px) and (max-width: 1430px) {
margin-top: 42px;
}
`; `;
export const PlanTitleDescription = styled(Typography)` export const PlanTitleDescription = styled(Typography)`
font-family: ${selectedTheme.fonts.textFont}; font-family: ${selectedTheme.fonts.textFont};
color: ${(props) => color: ${(props) =>
props.highlighted ? "white" : selectedTheme.colors.primaryPurple}; props.highlighted ? "white" : selectedTheme.colors.primaryPurple};


@media (max-width: 1430px) {
font-size: 22px;
}

@media (max-width: 600px) { @media (max-width: 600px) {
font-size: 18px; font-size: 18px;
} }
? selectedTheme.colors.primaryYellow ? selectedTheme.colors.primaryYellow
: selectedTheme.colors.primaryPurple}; : selectedTheme.colors.primaryPurple};


@media (max-width: 1430px) {
max-width: 34px;
max-height: 20px;
white-space: nowrap;
overflow: hidden;
}

@media (max-width: 600px) { @media (max-width: 600px) {
font-size: 14px; font-size: 14px;
max-width: 30px;
max-height: 20px;
white-space: nowrap;
overflow: hidden;
} }
`; `;
export const PlanDetailDescription = styled(Typography)` export const PlanDetailDescription = styled(Typography)`
} }
} }


@media (min-width: 1194px) and (max-width: 1430px) {
left: calc(50% - 12px);
}

@media (max-width: 600px) { @media (max-width: 600px) {
svg { svg {
width: 18px; width: 18px;

+ 13
- 0
src/components/Prices/PricesComponent.styled.js 查看文件

padding: 72px; padding: 72px;
background: white; background: white;


@media (max-width: 1430px) {
padding: 51px;
}

@media (max-width: 600px) { @media (max-width: 600px) {
padding: 36px; padding: 36px;
} }
margin-top: 46px; margin-top: 46px;
margin-bottom: 36px; margin-bottom: 36px;


@media (max-width: 1430px) {
gap: 27px;
}

@media (max-width: 1193px) {
flex-direction: column;
align-items: center;
}

@media (max-width: 600px) { @media (max-width: 600px) {
flex-direction: column; flex-direction: column;
gap: 27px; gap: 27px;

+ 4
- 0
src/components/PrivacyPolicy/PrivacyPolicyComponent.styled.js 查看文件

export const PrivacyPolicyContainer = styled(Box)` export const PrivacyPolicyContainer = styled(Box)`
margin: 72px; margin: 72px;


@media (max-width: 834px) {
margin: 45px;
}

@media (max-width: 600px) { @media (max-width: 600px) {
margin: 36px 36px 69px 36px; margin: 36px 36px 69px 36px;
} }

+ 11
- 4
src/components/Profile/ProfileOffers/ProfileOffers.js 查看文件

const dispatch = useDispatch(); const dispatch = useDispatch();
const searchRef = useRef(null); const searchRef = useRef(null);


useEffect(() => {
return () => {
dispatch(setProfileOffers([]));
};
}, []);

useEffect(() => { useEffect(() => {
dispatch( dispatch(
fetchProfileOffers({ fetchProfileOffers({
sortOption: sortOption, sortOption: sortOption,
append: isMobile && append, append: isMobile && append,
page: paging.currentPage, page: paging.currentPage,
isAdmin: props?.isAdmin,
}) })
); );
setAppend(true); setAppend(true);
<ProfileOffersHeaderSkeleton /> <ProfileOffersHeaderSkeleton />
{isMobile ? ( {isMobile ? (
<SkeletonContainer> <SkeletonContainer>
<SkeletonOfferCard vertical />
<SkeletonOfferCard vertical />
<SkeletonOfferCard vertical />
<SkeletonOfferCard vertical skeleton />
<SkeletonOfferCard vertical skeleton />
<SkeletonOfferCard vertical skeleton />
</SkeletonContainer> </SkeletonContainer>
) : ( ) : (
<> <>
{arrayForMapping.map((item, index) => ( {arrayForMapping.map((item, index) => (
<SkeletonOfferCard key={index} />
<SkeletonOfferCard key={index} skeleton />
))} ))}
</> </>
)} )}

+ 4
- 4
src/components/RichTextComponent/BlockButton/BlockButton.js 查看文件

import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Button, Icon } from "@mui/material";
import { useSlate } from "slate-react"; import { useSlate } from "slate-react";
import { Editor, Transforms, Element as SlateElement } from "slate"; import { Editor, Transforms, Element as SlateElement } from "slate";
import { BlockButtonContainer, BlockButtonIcon } from "./BlockButton.styled";
const LIST_TYPES = ["numbered-list", "bulleted-list"]; const LIST_TYPES = ["numbered-list", "bulleted-list"];
const TEXT_ALIGN_TYPES = ["left", "center", "right", "justify"]; const TEXT_ALIGN_TYPES = ["left", "center", "right", "justify"];
const BlockButton = ({ format, icon }) => { const BlockButton = ({ format, icon }) => {
const editor = useSlate(); const editor = useSlate();
return ( return (
<Button
<BlockButtonContainer
active={isBlockActive( active={isBlockActive(
editor, editor,
format, format,
toggleBlock(editor, format); toggleBlock(editor, format);
}} }}
> >
<Icon>{icon}</Icon>
</Button>
<BlockButtonIcon>{icon}</BlockButtonIcon>
</BlockButtonContainer>
); );
}; };
const isBlockActive = (editor, format, blockType = "type") => { const isBlockActive = (editor, format, blockType = "type") => {

+ 27
- 0
src/components/RichTextComponent/BlockButton/BlockButton.styled.js 查看文件

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

export const BlockButtonContainer = styled(Box)`
display: inline;
cursor: pointer;
`;
export const BlockButtonIcon = styled(Box)`
font-size: 16px;
font-family: "Source Code Pro";
${(props) =>
props.format === "bold"
? `font-weight: bold;`
: props.format === "italic"
? "font-style: italic;"
: props.format === "underline"
? `text-decoration: underline;`
: ""}
color: ${selectedTheme.colors.primaryGrayText};
line-height: 20px;
${(props) =>
props.active &&
css`
color: ${selectedTheme.colors.primaryText};
`}
`;

+ 43
- 0
src/components/RichTextComponent/ColorButton/ColorButton.js 查看文件

import React from "react";
import PropTypes from "prop-types";
import { useSlate } from "slate-react";
import { Editor } from "slate";
import { MarkButtonContainer, MarkButtonIcon } from "../MarkButton/MarkButton.styled";

const toggleMark = (editor, format) => {
const isActive = isMarkActive(editor, format);

if (isActive) {
Editor.removeMark(editor, format);
} else {
Editor.addMark(editor, format, true);
}
};

const isMarkActive = (editor, format) => {
const marks = Editor.marks(editor);
return marks ? marks[format] === true : false;
};

const ColorButton = ({ format, icon }) => {
const editor = useSlate();
return (
<MarkButtonContainer
active={isMarkActive(editor, format)}
format={format}
onMouseDown={(event) => {
event.preventDefault();
toggleMark(editor, format);
}}
>
<MarkButtonIcon format={format} active={isMarkActive(editor, format)}>
{icon}
</MarkButtonIcon>
</MarkButtonContainer>
);
};
ColorButton.propTypes = {
format: PropTypes.any,
icon: PropTypes.any,
};
export default ColorButton;

+ 7
- 4
src/components/RichTextComponent/MarkButton/MarkButton.js 查看文件

import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Button, Icon } from "@mui/material";
import { useSlate } from "slate-react"; import { useSlate } from "slate-react";
import { Editor } from "slate"; import { Editor } from "slate";
import { MarkButtonContainer, MarkButtonIcon } from "./MarkButton.styled";


const toggleMark = (editor, format) => { const toggleMark = (editor, format) => {
const isActive = isMarkActive(editor, format); const isActive = isMarkActive(editor, format);
const MarkButton = ({ format, icon }) => { const MarkButton = ({ format, icon }) => {
const editor = useSlate(); const editor = useSlate();
return ( return (
<Button
<MarkButtonContainer
active={isMarkActive(editor, format)} active={isMarkActive(editor, format)}
format={format}
onMouseDown={(event) => { onMouseDown={(event) => {
event.preventDefault(); event.preventDefault();
toggleMark(editor, format); toggleMark(editor, format);
}} }}
> >
<Icon>{`${icon} ${isMarkActive(editor, format)}`}</Icon>
</Button>
<MarkButtonIcon format={format} active={isMarkActive(editor, format)}>
{icon}
</MarkButtonIcon>
</MarkButtonContainer>
); );
}; };
MarkButton.propTypes = { MarkButton.propTypes = {

+ 27
- 0
src/components/RichTextComponent/MarkButton/MarkButton.styled.js 查看文件

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

export const MarkButtonContainer = styled(Box)`
display: inline;
cursor: pointer;
`;
export const MarkButtonIcon = styled(Box)`
font-size: 16px;
font-family: "Source Code Pro";
${(props) =>
props.format === "bold"
? `font-weight: bold;`
: props.format === "italic"
? "font-style: italic;"
: props.format === "underline"
? `text-decoration: underline;`
: ""}
color: ${selectedTheme.colors.primaryGrayText};
line-height: 20px;
${(props) =>
props.active &&
css`
color: ${selectedTheme.colors.primaryText};
`}
`;

+ 80
- 60
src/components/RichTextComponent/RichTextComponent.js 查看文件

import { createEditor } from "slate"; import { createEditor } from "slate";


import RichTextElement from "./RichTextElement/RichTextElement"; import RichTextElement from "./RichTextElement/RichTextElement";
import BlockButton from "./BlockButton/BlockButton";
import MarkButton from "./MarkButton/MarkButton"; import MarkButton from "./MarkButton/MarkButton";
import RichTextLeaf from "./RichTextLeaf/RichTextLeaf"; import RichTextLeaf from "./RichTextLeaf/RichTextLeaf";
import { ReactComponent as BulletedList } from "../../assets/images/svg/bulleted-list.svg";
import {
EditableContainer,
EditableInnerContainer,
RichTextComponentContainer,
SlateButtonsContainer,
} from "./RichTextComponent.styled";
import { useState } from "react";
import BlockButton from "./BlockButton/BlockButton";
import ColorButton from "./ColorButton/ColorButton";
import selectedTheme from "../../themes";

const COLORS = [
{
color: selectedTheme.colors.colorPicker.darkGray,
i18key: "colorPicker.darkGray",
},
{
color: selectedTheme.colors.colorPicker.gray,
i18key: "colorPicker.gray",
},
{
color: selectedTheme.colors.colorPicker.yellow,
i18key: "colorPicker.yellow",
},
{
color: selectedTheme.colors.colorPicker.purple,
i18key: "colorPicker.purple",
},
{
color: selectedTheme.colors.colorPicker.pink,
i18key: "colorPicker.pink",
},
];


const RichTextComponent = () => { const RichTextComponent = () => {
const [color, setColor] = useState(COLORS[0]);
const [value, setValue] = useState([
{
type: "paragraph",
children: [{ text: "" }],
},
]);
const renderElement = useCallback( const renderElement = useCallback(
(props) => <RichTextElement {...props} />, (props) => <RichTextElement {...props} />,
[] []
const editor = useMemo(() => withReact(createEditor()), []); const editor = useMemo(() => withReact(createEditor()), []);


return ( return (
<Slate editor={editor} value={initialValue}>
<div>
<MarkButton format="bold" icon="format_bold" />
<MarkButton format="italic" icon="format_italic" />
<MarkButton format="underline" icon="format_underlined" />
<MarkButton format="code" icon="code" />
<BlockButton format="heading-one" icon="looks_one" />
<BlockButton format="heading-two" icon="looks_two" />
<BlockButton format="block-quote" icon="format_quote" />
<BlockButton format="numbered-list" icon="format_list_numbered" />
<BlockButton format="bulleted-list" icon="format_list_bulleted" />
<BlockButton format="left" icon="format_align_left" />
<BlockButton format="center" icon="format_align_center" />
<BlockButton format="right" icon="format_align_right" />
<BlockButton format="justify" icon="format_align_justify" />
</div>
<Editable
renderElement={renderElement}
renderLeaf={renderLeaf}
placeholder="Enter some rich text…"
spellCheck
autoFocus
/>
</Slate>
<RichTextComponentContainer>
<Slate
editor={editor}
value={value}
onChange={(newValue) => {
console.log(newValue);
setValue(newValue);
}}
>
<SlateButtonsContainer>
<MarkButton format="bold" icon="B" />
<MarkButton format="italic" icon="I" />
<MarkButton format="underline" icon="U" />
<BlockButton format="bulleted-list" icon={<BulletedList />} />
<ColorButton
format="color"
currentColor={color}
setCurrentColor={setColor}
colorValues={COLORS}
/>
</SlateButtonsContainer>
<EditableContainer>
<EditableInnerContainer>
<Editable
renderElement={renderElement}
renderLeaf={renderLeaf}
spellCheck
autoFocus
/>
</EditableInnerContainer>
</EditableContainer>
</Slate>
</RichTextComponentContainer>
); );
}; };


const initialValue = [
{
type: "paragraph",
children: [
{ text: "This is editable " },
{ text: "rich", bold: true },
{ text: " text, " },
{ text: "much", italic: true },
{ text: " better than a " },
{ text: "<textarea>", code: true },
{ text: "!" },
],
},
{
type: "paragraph",
children: [
{
text: "Since it's rich text, you can do things like turn a selection of text ",
},
{ text: "bold", bold: true },
{
text: ", or add a semantically rendered block quote in the middle of the page, like this:",
},
],
},
{
type: "block-quote",
children: [{ text: "A wise quote." }],
},
{
type: "paragraph",
align: "center",
children: [{ text: "Try it out for yourself!" }],
},
];
// const initialValue = [
// {
// type: "paragraph",
// children: [{ text: "" }],
// },
// ];


export default RichTextComponent; export default RichTextComponent;

+ 46
- 0
src/components/RichTextComponent/RichTextComponent.styled.js 查看文件

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

export const SlateButtonsContainer = styled(Box)`
display: flex;
flex-direction: row;
height: 36px;
background-color: ${selectedTheme.colors.stylingTextBackground};
padding: 8px 16px;
border-radius: 4px;
gap: 20px;
`;
export const RichTextComponentContainer = styled(Box)`
width: 100%;
position: relative;
top: 14px;
height: 140px;
border: 1px solid ${selectedTheme.colors.borderNormal};
margin-bottom: 14px;
`;
export const EditableContainer = styled(Box)`
max-height: 104px;
overflow: auto;
&::-webkit-scrollbar {
width: 5px;
height: 5px;
}
&::-webkit-scrollbar-track {
background: #ddd;
}
&::-webkit-scrollbar-thumb {
background: #777;
}
scrollbar-width: thin;
scrollbar-color: #ddd;
`;
export const EditableInnerContainer = styled(Box)`
padding: 10px 16px;
& > div {
min-height: 84px;
}
& * {
font-family: ${selectedTheme.fonts.textFont};
}
`;

+ 5
- 4
src/components/RichTextComponent/RichTextElement/RichTextElement.js 查看文件

import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { ListContainer } from "./RichTextElement.styled";


const RichTextElement = ({ attributes, children, element }) => { const RichTextElement = ({ attributes, children, element }) => {
const style = { textAlign: element.align }; const style = { textAlign: element.align };
); );
case "bulleted-list": case "bulleted-list":
return ( return (
<ul style={style} {...attributes}>
<ListContainer style={style} {...attributes}>
{children} {children}
</ul>
</ListContainer>
); );
case "heading-one": case "heading-one":
return ( return (
); );
default: default:
return ( return (
<p style={style} {...attributes}>
<div style={style} {...attributes}>
{children} {children}
</p>
</div>
); );
} }
}; };

+ 9
- 0
src/components/RichTextComponent/RichTextElement/RichTextElement.styled.js 查看文件

import { Box } from "@mui/material";
import styled from "styled-components";

export const ListContainer = styled(Box)`
padding-left:
& > div {
display: list-item;
}
`

+ 1
- 1
src/components/RichTextComponent/RichTextLeaf/RichTextLeaf.js 查看文件

} }


if (leaf.italic) { if (leaf.italic) {
children = <em>{children}</em>;
children = <i>{children}</i>;
} }


if (leaf.underline) { if (leaf.underline) {

+ 17
- 13
src/components/UserReviews/ReviewsSorting/ReviewsSorting.js 查看文件

const [value, setValue] = useState(); const [value, setValue] = useState();
const changeValue = (event) => { const changeValue = (event) => {
if (props.isAdmin) { if (props.isAdmin) {
// if(event.target.value.value === 1) {
// dispatch(setReviews(reviews.givenReviews));
// } else {
// dispatch(setReviews(reviews.receivedReviews));
// }


// if(event.target.value.value === 1) {
// dispatch(setReviews(reviews.givenReviews));
// } else {
// dispatch(setReviews(reviews.receivedReviews));
// }
} else { } else {
dispatch( dispatch(
setReviews( setReviews(
const sortEnum = useMemo(() => { const sortEnum = useMemo(() => {
if (props.isAdmin) return sortAdminEnum; if (props.isAdmin) return sortAdminEnum;
return reviewSortEnum; return reviewSortEnum;
});
}, [props.isAdmin]);

const initialSortOption = useMemo(() => {
if (props.isAdmin) return sortAdminEnum.GIVEN;
return reviewSortEnum.INITIAL;
}, [props.isAdmin]);

useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
sortValue: value, sortValue: value,
hasGivenReview: value?.value === sortAdminEnum.GIVEN.value
}))
hasGivenReview: value?.value === sortAdminEnum.GIVEN.value,
}));
return ( return (
<HeaderSelect <HeaderSelect
value={value || sortEnum.INITIAL}
value={value || initialSortOption}
IconComponent={DownArrowIcon} IconComponent={DownArrowIcon}
onChange={changeValue} onChange={changeValue}
> >
<SelectOption style={{ display: "none" }} value={sortEnum.INITIAL}>
{sortEnum.INITIAL.mainText}
<SelectOption style={{ display: "none" }} value={initialSortOption}>
{initialSortOption.mainText}
</SelectOption> </SelectOption>
{Object.keys(sortEnum).map((property) => { {Object.keys(sortEnum).map((property) => {
if (sortEnum[property].value === 0) return; if (sortEnum[property].value === 0) return;

+ 0
- 0
src/components/UserReviews/UserReviews.js 查看文件


部分文件因为文件数量过多而无法显示

正在加载...
取消
保存