| REACT_APP_BASE_API_URL=http://localhost:3001/ |
| # misc | # misc | ||||
| .DS_Store | .DS_Store | ||||
| .env | |||||
| .env.local | .env.local | ||||
| .env.development.local | .env.development.local | ||||
| .env.test.local | .env.test.local |
| { | { | ||||
| "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", |
| 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" |
| 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> |
| <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> |
| 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> | ||||
| ); | ); | ||||
| }); | }); |
| 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; | ||||
| } | } |
| 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) { |
| 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) { |
| 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(); |
| 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(); |
| 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; |
| 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> |
| 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> |
| 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> | ||||
| ); | ); | ||||
| }; | }; |
| 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 ( |
| 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; |
| 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; |
| 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; |
| }, | }, | ||||
| })); | })); | ||||
| 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; |
| 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> | ||||
| ); | ); | ||||
| }; | }; |
| 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; |
| (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, |
| (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, |
| 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, |
| 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> |
| <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; |
| 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> |
| 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} | |||||
| /> | |||||
| )} | )} | ||||
| </> | </> | ||||
| ); | ); |
| 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; | |||||
| } | |||||
| ` | ` |
| 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)` |
| 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> |
| 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} | ||||
| /> | /> |
| border-radius: 2px; | border-radius: 2px; | ||||
| @media screen and (max-width: 600px) { | @media screen and (max-width: 600px) { | ||||
| margin-right: 13px; | |||||
| /* margin-right: 13px; */ | |||||
| } | } | ||||
| `; | `; | ||||
| 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} | ||||
| /> | /> |
| 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; | |||||
| ` |
| 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; |
| 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)``; |
| }) | }) | ||||
| ); | ); | ||||
| } 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, | |||||
| }) | }) | ||||
| ); | ); | ||||
| }; | }; |
| ); | ); | ||||
| }; | }; | ||||
| 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 ? ( |
| 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 /> |
| 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)` |
| 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, | ||||
| }; | }; | ||||
| ...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, |
| 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; |
| 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}; | |||||
| } | } | ||||
| `; | `; |
| 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; |
| <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, |
| 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({ |
| 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, |
| 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; | |||||
| } | |||||
| ` |
| 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} |
| 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)` |
| : 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> | ||||
| ); | ); | ||||
| }; | }; |
| 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; | |||||
| } | |||||
| } | |||||
| `; | `; |
| 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 /> |
| 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> |
| 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, | ||||
| }; | }; |
| /* 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; |
| 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, | ||||
| }; | }; | ||||
| 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; | |||||
| } | |||||
| `; | `; |
| 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> | ||||
| ); | ); |
| 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, | ||||
| }; | }; | ||||
| 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} | |||||
| /> | /> | ||||
| ); | ); | ||||
| })} | })} |
| 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); | |||||
| `; | `; |
| 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; |
| 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}> |
| 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> |
| 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; | |||||
| } | |||||
| `; |
| 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); |
| 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> |
| 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, |
| <LocationIcon /> | <LocationIcon /> | ||||
| </DetailIcon> | </DetailIcon> | ||||
| <DetailText ismyprofile={props.isMyProfile}> | <DetailText ismyprofile={props.isMyProfile}> | ||||
| {offer.offer?.location?.city} | |||||
| {offer?.location?.city} | |||||
| </DetailText> | </DetailText> | ||||
| </DetailContainer> | </DetailContainer> | ||||
| ); | ); |
| <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> | ||||
| ); | ); |
| 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> |
| 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> |
| 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> |
| 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) => ( |
| 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} | ||||
| /> | /> | ||||
| ); | ); |
| }; | }; | ||||
| const handleApiResponseSuccess = () => { | const handleApiResponseSuccess = () => { | ||||
| if (clickedOnNext) { | |||||
| if (clickedOnNext && props?.showSecondButton) { | |||||
| formik.resetForm(); | formik.resetForm(); | ||||
| inputRef.current.focus(); | inputRef.current.focus(); | ||||
| } else closeModalHandler(); | } else closeModalHandler(); |
| 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 ( |
| 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 = () => { |
| ${(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; |
| 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; |
| 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; | ||||
| } | } |
| 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 /> | |||||
| ))} | ))} | ||||
| </> | </> | ||||
| )} | )} |
| 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") => { |
| 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}; | |||||
| `} | |||||
| `; |
| 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; |
| 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 = { |
| 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}; | |||||
| `} | |||||
| `; |
| 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; |
| 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}; | |||||
| } | |||||
| `; |
| 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> | |||||
| ); | ); | ||||
| } | } | ||||
| }; | }; |
| import { Box } from "@mui/material"; | |||||
| import styled from "styled-components"; | |||||
| export const ListContainer = styled(Box)` | |||||
| padding-left: | |||||
| & > div { | |||||
| display: list-item; | |||||
| } | |||||
| ` |
| } | } | ||||
| if (leaf.italic) { | if (leaf.italic) { | ||||
| children = <em>{children}</em>; | |||||
| children = <i>{children}</i>; | |||||
| } | } | ||||
| if (leaf.underline) { | if (leaf.underline) { |
| 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; |