| @@ -98,6 +98,7 @@ export const OfferTitle = styled(Typography)` | |||
| display: -webkit-box; | |||
| -webkit-line-clamp: 1; | |||
| -webkit-box-orient: vertical; | |||
| text-overflow: ellipsis; | |||
| max-height: 33px; | |||
| margin-bottom: 28px; | |||
| @media (max-width: 550px) { | |||
| @@ -109,10 +110,12 @@ export const OfferTitle = styled(Typography)` | |||
| ` | |||
| display: flex; | |||
| flex: none; | |||
| max-height: 44px; | |||
| position: relative; | |||
| line-height: 22px; | |||
| margin-top: 5px; | |||
| font-size: 18px; | |||
| margin-bottom: 0px; | |||
| `} | |||
| } | |||
| @@ -134,8 +137,6 @@ export const OfferAuthorName = styled(Typography)` | |||
| ` | |||
| line-height: 19px; | |||
| font-size: 14px; | |||
| position: absolute; | |||
| bottom: 125px; | |||
| `} | |||
| } | |||
| `; | |||
| @@ -148,9 +149,7 @@ export const OfferLocation = styled(Typography)` | |||
| props.vertical && | |||
| ` | |||
| font-size: 12px; | |||
| margin-top: 5px; | |||
| position: absolute; | |||
| bottom: 105px; | |||
| margin-top: -17px; | |||
| `} | |||
| `; | |||
| export const OfferDetails = styled(Box)` | |||
| @@ -173,8 +172,8 @@ export const OfferCategory = styled(Box)` | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| position: absolute; | |||
| bottom: 60px; | |||
| // position: absolute; | |||
| // bottom: 60px; | |||
| `} | |||
| `; | |||
| export const OfferPackage = styled(Box)` | |||
| @@ -303,9 +302,9 @@ export const RemoveIconContainer = styled(MessageIcon)` | |||
| @media screen and (max-width: 600px) { | |||
| display: block; | |||
| position: absolute; | |||
| left: 59px; | |||
| top: 325px; | |||
| position: ${props => props.vertical && "absolute"}; | |||
| top: ${props => props.vertical && "325px"}; | |||
| left: ${props => props.vertical && "59px"}; | |||
| } | |||
| `; | |||
| export const RemoveIcon = styled(Remove)``; | |||
| @@ -317,8 +316,9 @@ export const EditIconContainer = styled(MessageIcon)` | |||
| @media screen and (max-width: 600px) { | |||
| position: absolute; | |||
| display: block; | |||
| top: 325px; | |||
| left: 18px; | |||
| right: ${props => !props.vertical && "64px"}; | |||
| top: ${props => props.vertical && "325px"}; | |||
| left: ${props => props.vertical && "18px"}; | |||
| } | |||
| `; | |||
| export const EditIcon = styled(Edit)``; | |||
| @@ -341,6 +341,10 @@ export const LikeIcon = styled(Like)` | |||
| ? selectedTheme.colors.primaryPurpleDisabled | |||
| : selectedTheme.colors.primaryPurple}; | |||
| } | |||
| @media (max-width: 600px) { | |||
| position: relative; | |||
| top: -1px; | |||
| } | |||
| `; | |||
| export const PinIcon = styled(Pin)` | |||
| position: absolute; | |||
| @@ -39,7 +39,10 @@ const ProfileCard = () => { | |||
| const dispatch = useDispatch(); | |||
| const profileFromRedux = useSelector(selectProfile); | |||
| const userId = useSelector(selectUserId); | |||
| const idProfile = routeMatch.params.idProfile; | |||
| const idProfile = useMemo(() => { | |||
| console.log("routematch", routeMatch); | |||
| return routeMatch.params.idProfile; | |||
| }, [routeMatch.params.idProfile]); | |||
| const { t } = useTranslation(); | |||
| const profile = useMemo(() => { | |||
| @@ -51,17 +54,19 @@ const ProfileCard = () => { | |||
| return companyData; | |||
| }, [profileFromRedux]); | |||
| useEffect(() => { | |||
| if (idProfile?.length > 0) { | |||
| reFetchProfile(); | |||
| } | |||
| }, [idProfile]); | |||
| useEffect(() => { | |||
| setIsMyProfile(userId === idProfile); | |||
| }, [userId, idProfile]); | |||
| const reFetchProfile = () => { | |||
| dispatch(fetchProfile(idProfile)); | |||
| dispatch(fetchProfileOffers(idProfile)); | |||
| if (userId === idProfile) setIsMyProfile(true); | |||
| }; | |||
| let percentOfSucceededExchanges; | |||
| @@ -44,6 +44,10 @@ export const MessageButton = styled(EditButton)` | |||
| background: ${selectedTheme.colors.primaryPurple}; | |||
| width: 40px; | |||
| height: 40px; | |||
| @media (max-width: 600px) { | |||
| width: 32px; | |||
| height: 32px; | |||
| } | |||
| `; | |||
| export const ProfileCardWrapper = styled(Card)` | |||
| @@ -247,16 +251,17 @@ export const MessageIcon = styled(Mail)` | |||
| width: 19.5px; | |||
| height: 19.5px; | |||
| position: relative; | |||
| left: 2.5px; | |||
| top: 3.5px; | |||
| left: 1px; | |||
| top: 3px; | |||
| & path { | |||
| stroke: ${selectedTheme.colors.primaryYellow}; | |||
| } | |||
| @media (max-width: 600px) { | |||
| width: 14px; | |||
| height: 14px; | |||
| right: 0.5px; | |||
| } | |||
| width: 16px; | |||
| height: 16px; | |||
| left: -1px; | |||
| top: 1px; | |||
| } | |||
| `; | |||
| export const ProfileInfoContainer = styled(Grid)` | |||
| @@ -10,7 +10,7 @@ import { useTranslation } from "react-i18next"; | |||
| const ReviewDetails = (props) => { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <ReviewDetailsContainer sx={{ pl: 2, pb: 2 }}> | |||
| <ReviewDetailsContainer> | |||
| <ReviewDetailsText variant="body2" sx={{ display: "block" }}> | |||
| {t("reviews.isCorrectCommunication") + ": "} | |||
| <ReviewDetailsValue> | |||
| @@ -2,16 +2,21 @@ import { Grid, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../themes"; | |||
| export const ReviewDetailsContainer = styled(Grid)``; | |||
| export const ReviewDetailsContainer = styled(Grid)` | |||
| padding-bottom: 1rem; | |||
| `; | |||
| export const ReviewDetailsText = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-size: 12px; | |||
| color: ${selectedTheme.colors.primaryDarkText}; | |||
| font-style: italic; | |||
| letter-spacing: 0.02em; | |||
| line-height: 16px; | |||
| `; | |||
| export const ReviewDetailsValue = styled(Typography)` | |||
| color: ${selectedTheme.colors.primaryPurple}; | |||
| font-style: normal; | |||
| line-height: 16px; | |||
| font-size: 12px; | |||
| font-weight: 600; | |||
| `; | |||
| @@ -6,7 +6,6 @@ export const ReviewOfferContainer = styled(Box)` | |||
| display: flex; | |||
| flex-direction: row; | |||
| gap: 18px; | |||
| padding-left: 16px; | |||
| `; | |||
| export const ReviewOfferImage = styled.img` | |||
| width: 54px; | |||
| @@ -12,13 +12,7 @@ import { reviewEnum } from "../../../../enums/reviewEnum"; | |||
| const ReviewQuote = (props) => { | |||
| return ( | |||
| <ReviewQuoteContainer | |||
| container | |||
| direction="row" | |||
| justifyContent="start" | |||
| alignItems="center" | |||
| spacing={2} | |||
| > | |||
| <ReviewQuoteContainer> | |||
| <ThumbContainer item> | |||
| {props.isSuccessfulSwap.toLowerCase() === | |||
| reviewEnum.YES.mainText.toLowerCase() ? ( | |||
| @@ -1,15 +1,20 @@ | |||
| import { Grid, Typography } from "@mui/material"; | |||
| import { Box, Grid, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import ThumbUpIcon from "@mui/icons-material/ThumbUp"; | |||
| import ThumbDownIcon from "@mui/icons-material/ThumbDown"; | |||
| export const ReviewQuoteContainer = styled(Grid)` | |||
| export const ReviewQuoteContainer = styled(Box)` | |||
| position: relative; | |||
| left: 8px; | |||
| padding-left: 2px; | |||
| padding-top: 2px; | |||
| padding-bottom: 2px; | |||
| margin: 0; | |||
| display: flex; | |||
| flex-direction: row; | |||
| margin-top: 18px; | |||
| align-items: center; | |||
| justify-content: start; | |||
| margin-bottom: 15px; | |||
| `; | |||
| export const ThumbContainer = styled(Grid)` | |||
| max-width: 20px; | |||
| @@ -17,15 +22,15 @@ export const ThumbContainer = styled(Grid)` | |||
| export const ReviewQuoteTextContainer = styled(Grid)``; | |||
| export const ReviewQuoteText = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-size: 12px; | |||
| font-size: 16px; | |||
| color: ${selectedTheme.colors.primaryDarkText}; | |||
| position: relative; | |||
| left: 10px; | |||
| `; | |||
| export const ThumbUp = styled(ThumbUpIcon)` | |||
| position: relative; | |||
| left: -8px; | |||
| `; | |||
| export const ThumbDown = styled(ThumbDownIcon)` | |||
| position: relative; | |||
| left: -8px; | |||
| top: 3px; | |||
| `; | |||
| @@ -8,17 +8,26 @@ import { | |||
| ProfileImageContainer, | |||
| ProfileName, | |||
| } from "./ReviewerProfile.styled"; | |||
| import history from "../../../../store/utils/history"; | |||
| import { replaceInRoute } from "../../../../util/helpers/routeHelpers"; | |||
| import { PROFILE_PAGE } from "../../../../constants/pages"; | |||
| const ReviewerProfile = (props) => { | |||
| const { isMobile } = useIsMobile(); | |||
| const routeToUser = () => { | |||
| history.push(replaceInRoute(PROFILE_PAGE, { | |||
| idProfile: props.userId | |||
| })) | |||
| } | |||
| return ( | |||
| <ProfileContainer> | |||
| <ProfileImageContainer> | |||
| <ProfileImage | |||
| onClick={routeToUser} | |||
| src={getImageUrl(props.profileImage, variants.reviewCard, isMobile)} | |||
| /> | |||
| </ProfileImageContainer> | |||
| <ProfileName>{props.profileName}</ProfileName> | |||
| <ProfileName onClick={routeToUser}>{props.profileName}</ProfileName> | |||
| </ProfileContainer> | |||
| ); | |||
| }; | |||
| @@ -26,6 +35,7 @@ const ReviewerProfile = (props) => { | |||
| ReviewerProfile.propTypes = { | |||
| profileName: PropTypes.string, | |||
| profileImage: PropTypes.string, | |||
| userId: PropTypes.string, | |||
| }; | |||
| export default ReviewerProfile; | |||
| @@ -6,6 +6,7 @@ export const ProfileImage = styled.img` | |||
| width: 54px; | |||
| height: 54px; | |||
| border-radius: 100%; | |||
| cursor: pointer; | |||
| ` | |||
| export const ProfileImageContainer = styled(Box)` | |||
| width: 54px; | |||
| @@ -16,10 +17,13 @@ export const ProfileImageContainer = styled(Box)` | |||
| export const ProfileName = styled(Typography)` | |||
| font-weight: 600; | |||
| font-size: 16px; | |||
| position: relative; | |||
| top: 12px; | |||
| cursor: pointer; | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| color: ${selectedTheme.colors.primaryPurple}; | |||
| ` | |||
| export const ProfileContainer = styled(ListItem)` | |||
| align-items: flex-start; | |||
| margin-top: 2px; | |||
| padding: 0; | |||
| ` | |||
| @@ -8,7 +8,6 @@ import ReviewQuote from "./ReviewQuote/ReviewQuote"; | |||
| import ReviewDetails from "./ReviewDetails/ReviewDetails"; | |||
| const UserReviewsCard = (props) => { | |||
| const review = useMemo(() => { | |||
| if (props.givingReview) { | |||
| return { | |||
| @@ -35,6 +34,7 @@ const UserReviewsCard = (props) => { | |||
| return { | |||
| name: props.review.userWhoGaveReview.name, | |||
| image: props.review.userWhoGaveReview.image, | |||
| userId: props.review.userId, | |||
| isGoodCommunication, | |||
| isSuccessfulSwap, | |||
| quote: props?.review?.message, | |||
| @@ -45,7 +45,11 @@ const UserReviewsCard = (props) => { | |||
| return ( | |||
| <ReviewContainer key={review?.image}> | |||
| <ReviewerProfile profileName={review.name} profileImage={review.image} /> | |||
| <ReviewerProfile | |||
| profileName={review.name} | |||
| profileImage={review.image} | |||
| userId={review.userId} | |||
| /> | |||
| <ReviewQuote | |||
| isSuccessfulSwap={review?.isSuccessfulSwap} | |||
| quote={review?.quote} | |||
| @@ -7,7 +7,7 @@ export const ReviewsBox = styled(Box)` | |||
| width: 100%; | |||
| height: calc(100% - 90px); | |||
| max-height: 100vh; | |||
| padding-bottom: 20px; | |||
| padding-top: 20px; | |||
| @media (max-width: 1200px) { | |||
| padding: 0; | |||
| } | |||
| @@ -75,5 +75,15 @@ export const NoReviewsAltText = styled(Typography)` | |||
| `; | |||
| export const ReviewContainer = styled(Box)` | |||
| padding-bottom: 20px; | |||
| padding-top: 18px; | |||
| &::after { | |||
| content: ""; | |||
| display: block; | |||
| border-bottom: 1px solid ${selectedTheme.colors.primaryIconBackgroundColor}; | |||
| width: calc(100% + 24.4px); | |||
| height: 1px; | |||
| position: relative; | |||
| left: -18px; | |||
| margin-top: 18px; | |||
| } | |||
| `; | |||
| @@ -5,7 +5,6 @@ import { | |||
| HeaderBack, | |||
| HeaderSelect, | |||
| ListContainer, | |||
| ListHeader, | |||
| SelectOption, | |||
| TitleSortContainer, | |||
| } from "./ChatColumn.styled"; | |||
| @@ -102,7 +101,6 @@ export const ChatColumn = () => { | |||
| })} | |||
| </HeaderSelect> | |||
| </TitleSortContainer> | |||
| <ListHeader enableSort={true} /> | |||
| <ListContainer> | |||
| {chats.map((item, index) => ( | |||
| <ChatCard key={index} chat={item} /> | |||
| @@ -16,7 +16,7 @@ export const ListContainer = styled(Box)` | |||
| gap: 12px; | |||
| @media (max-width: 600px) { | |||
| gap: 18px; | |||
| margin-top: 20px; | |||
| margin-top: 10px; | |||
| } | |||
| `; | |||
| @@ -60,6 +60,12 @@ export const PhoneIcon = styled(Phone)` | |||
| position: relative; | |||
| top: 2.5px; | |||
| left: 1.5px; | |||
| @media (max-width: 600px) { | |||
| width: 14px; | |||
| height: 14px; | |||
| top: -2px; | |||
| left: -2px; | |||
| } | |||
| ` | |||
| export const PhoneIconContainer = styled(IconButton)` | |||
| background-color: white; | |||
| @@ -74,4 +80,8 @@ export const PhoneIconContainer = styled(IconButton)` | |||
| background-color: ${selectedTheme.colors.primaryIconBackgroundColor}; | |||
| cursor: pointer; | |||
| } | |||
| @media (max-width: 600px) { | |||
| width: 32px; | |||
| height: 32px; | |||
| } | |||
| ` | |||
| @@ -45,7 +45,7 @@ const DirectChatNewMessage = (props) => { | |||
| }) | |||
| ); | |||
| if (props.chat?.chat?._id) { | |||
| if (!exchange.valid && props.chat?.offer?.offer?.userId === userId) { | |||
| if (!exchange.valid && exchange.seller.userId === userId) { | |||
| dispatch(validateExchange(exchange._id)); | |||
| } | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| import React, { useMemo } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| ButtonContainer, | |||
| CategoryHeaderIcon, | |||
| HeaderAltLocation, | |||
| HeaderButton, | |||
| @@ -9,10 +10,12 @@ import { | |||
| HeaderLocation, | |||
| HeaderOptions, | |||
| HeaderSelect, | |||
| HeaderText, | |||
| IconStyled, | |||
| MySwapsTitle, | |||
| RefreshIcon, | |||
| PageTitleContainer, | |||
| SelectOption, | |||
| SwapsIcon, | |||
| SwapsTitle, | |||
| } from "./Header.styled"; | |||
| import { ReactComponent as GridSquare } from "../../../assets/images/svg/offer-grid-square.svg"; | |||
| import { ReactComponent as GridLine } from "../../../assets/images/svg/offer-grid-line.svg"; | |||
| @@ -24,6 +27,9 @@ import { Tooltip } from "@mui/material"; | |||
| import SkeletonHeader from "./SkeletonHeader/SkeletonHeader"; | |||
| import { useSelector } from "react-redux"; | |||
| import { selectHeaderString } from "../../../store/selectors/filtersSelectors"; | |||
| import useIsMobile from "../../../hooks/useIsMobile"; | |||
| import { ArrowButton } from "../../Buttons/ArrowButton/ArrowButton"; | |||
| import history from "../../../store/utils/history"; | |||
| const DownArrow = (props) => ( | |||
| <IconStyled {...props}> | |||
| @@ -35,6 +41,7 @@ const Header = (props) => { | |||
| const { t } = useTranslation(); | |||
| const sorting = props.sorting; | |||
| const headerString = useSelector(selectHeaderString); | |||
| const { isMobile } = useIsMobile(); | |||
| // Changing header string on refresh or on load | |||
| const showAltString = useMemo(() => { | |||
| @@ -46,21 +53,15 @@ const Header = (props) => { | |||
| }, [sorting.selectedSortOption]); | |||
| const handleChangeSelect = (event) => { | |||
| // let chosenOption; | |||
| sorting.changeSorting(event.target.value); | |||
| // for (const sortOption in sortEnum) { | |||
| // if (sortEnum[sortOption].value === event.target.value) { | |||
| // chosenOption = sortEnum[sortOption]; | |||
| // sorting.changeSorting(chosenOption); | |||
| // } | |||
| // } | |||
| }; | |||
| const goBack = () => { | |||
| history.goBack(); | |||
| }; | |||
| return ( | |||
| <> | |||
| <SkeletonHeader | |||
| skeleton={props.skeleton} | |||
| /> | |||
| <SkeletonHeader skeleton={props.skeleton} /> | |||
| <HeaderContainer skeleton={props.skeleton}> | |||
| {/* Setting appropriate header title if page is market place or my offers */} | |||
| <Tooltip title={headerString}> | |||
| @@ -76,9 +77,10 @@ const Header = (props) => { | |||
| )} | |||
| </> | |||
| ) : ( | |||
| <MySwapsTitle> | |||
| <RefreshIcon /> {t("header.myOffers")} | |||
| </MySwapsTitle> | |||
| <ButtonContainer onClick={goBack}> | |||
| <ArrowButton side={"left"}></ArrowButton> | |||
| <HeaderText>{t("profile.backToHome")}</HeaderText> | |||
| </ButtonContainer> | |||
| )} | |||
| </> | |||
| </Tooltip> | |||
| @@ -122,9 +124,10 @@ const Header = (props) => { | |||
| } | |||
| IconComponent={DownArrow} | |||
| onChange={handleChangeSelect} | |||
| myOffers={props.myOffers} | |||
| > | |||
| <SelectOption style={{ display: "none" }} value="default"> | |||
| Sortiraj po | |||
| {t("reviews.sortBy")} | |||
| </SelectOption> | |||
| {Object.keys(sortEnum).map((property) => { | |||
| if (sortEnum[property].value === 0) return; | |||
| @@ -141,6 +144,14 @@ const Header = (props) => { | |||
| {/* ^^^^^^ */} | |||
| </HeaderOptions> | |||
| </HeaderContainer> | |||
| {isMobile && ( | |||
| <PageTitleContainer> | |||
| <SwapsIcon /> | |||
| <SwapsTitle> | |||
| {props.myOffers ? t("header.myOffers") : t("offer.offers")} | |||
| </SwapsTitle> | |||
| </PageTitleContainer> | |||
| )} | |||
| </> | |||
| ); | |||
| }; | |||
| @@ -1,4 +1,4 @@ | |||
| import { Box, MenuItem, Typography } from "@mui/material"; | |||
| import { Box, Link, MenuItem, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { IconButton } from "../../Buttons/IconButton/IconButton"; | |||
| @@ -9,7 +9,7 @@ import { ReactComponent as CategoryHeader } from "../../../assets/images/svg/cat | |||
| export const HeaderContainer = styled(Box)` | |||
| margin-top: 20px; | |||
| display: ${props => props.skeleton ? "none" : "flex"}; | |||
| display: ${(props) => (props.skeleton ? "none" : "flex")}; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| `; | |||
| @@ -32,7 +32,7 @@ export const HeaderLocation = styled(Box)` | |||
| } | |||
| } | |||
| @media (max-width: 600px) { | |||
| font-size: 14px; | |||
| font-size: 12px; | |||
| padding-top: 3px; | |||
| } | |||
| `; | |||
| @@ -65,6 +65,8 @@ export const HeaderSelect = styled(Select)` | |||
| width: 144px; | |||
| height: 30px; | |||
| font-size: 14px; | |||
| top: 60px; | |||
| left: ${props => props.myOffers ? "-7px" : "0"}; | |||
| } | |||
| `; | |||
| export const SelectItem = styled(MenuItem)` | |||
| @@ -115,4 +117,47 @@ export const MySwapsTitle = styled(Typography)` | |||
| left: 9px; | |||
| `; | |||
| export const CategoryHeaderIcon = styled(CategoryHeader)` | |||
| ` | |||
| @media (max-width: 600px) { | |||
| width: 12px; | |||
| height: 12px; | |||
| position: relative; | |||
| top: 1px; | |||
| } | |||
| `; | |||
| export const PageTitleContainer = styled(Box)` | |||
| position: relative; | |||
| left: 6px; | |||
| margin-top: 36px; | |||
| width: 100px; | |||
| `; | |||
| export const SwapsIcon = styled(RefreshIcon)` | |||
| width: 12px; | |||
| height: 12px; | |||
| top: 1px; | |||
| `; | |||
| export const SwapsTitle = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-size: 12px; | |||
| line-height: 16px; | |||
| color: ${selectedTheme.colors.primaryText}; | |||
| `; | |||
| export const ButtonContainer = styled(Link)` | |||
| width: fit-content; | |||
| cursor: pointer; | |||
| display: flex; | |||
| justify-content: start; | |||
| align-items: center; | |||
| gap: 12px; | |||
| text-decoration: none; | |||
| min-width: 200px; | |||
| `; | |||
| export const HeaderText = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| line-height: 22px; | |||
| font-size: 16px; | |||
| color: ${selectedTheme.colors.primaryPurple}; | |||
| border-bottom: 1px dotted ${selectedTheme.colors.primaryPurple}; | |||
| @media (max-width: 600px) { | |||
| font-size: 14px; | |||
| } | |||
| `; | |||
| @@ -6,5 +6,7 @@ export const MarketPlaceContainer = styled(Box)` | |||
| margin: 0 70px; | |||
| @media (max-width: 550px) { | |||
| margin: -30px 1.8rem; | |||
| margin-left: 18px; | |||
| margin-right: 18px; | |||
| } | |||
| `; | |||
| @@ -9,7 +9,7 @@ export const HeadersMyOffersContainer = styled(Box)``; | |||
| export const EndIcon = styled(Icon)``; | |||
| export const SearchIcon = styled(Search)` | |||
| position: relative; | |||
| top: 11px; | |||
| top: 10px; | |||
| left: 4px; | |||
| cursor: pointer; | |||
| color: ${selectedTheme.colors.primaryPurple}; | |||
| @@ -25,11 +25,17 @@ export const SearchIcon = styled(Search)` | |||
| `; | |||
| export const SearchInput = styled(TextField)` | |||
| & div { | |||
| height: 40px; | |||
| height: 46px; | |||
| background-color: white; | |||
| } | |||
| & div div input::placeholder { | |||
| font-style: italic; | |||
| font-size: 14px; | |||
| position: relative; | |||
| top: -2px; | |||
| } | |||
| @media (max-width: 600px) { | |||
| width: 90%; | |||
| width: 100%; | |||
| height: 36px; | |||
| } | |||
| `; | |||
| @@ -28,15 +28,15 @@ const Offers = (props) => { | |||
| return ( | |||
| <> | |||
| <FilterContainer | |||
| onClick={toggleFilters} | |||
| number={offers.filters.numOfFiltersChosen} | |||
| myOffers={props.myOffers} | |||
| > | |||
| <FilterIcon /> | |||
| </FilterContainer> | |||
| {!props.skeleton ? ( | |||
| <> | |||
| <FilterContainer | |||
| onClick={toggleFilters} | |||
| number={offers.filters.numOfFiltersChosen} | |||
| myOffers={props.myOffers} | |||
| > | |||
| <FilterIcon /> | |||
| </FilterContainer> | |||
| {props.myOffers && ( | |||
| <HeadersMyOffers | |||
| searchMyOffers={offers.search.searchOffers} | |||
| @@ -70,10 +70,7 @@ const Offers = (props) => { | |||
| ) : ( | |||
| <> | |||
| {arrayForMapping.map((item, index) => ( | |||
| <SkeletonOfferCard | |||
| key={index} | |||
| skeleton | |||
| /> | |||
| <SkeletonOfferCard key={index} skeleton /> | |||
| ))} | |||
| </> | |||
| )} | |||
| @@ -16,8 +16,8 @@ export const OffersContainer = styled(Box)` | |||
| `; | |||
| export const FilterContainer = styled(IconWithNumber)` | |||
| position: absolute; | |||
| top: ${props => props.myOffers ? "143px" : "93px"}; | |||
| right: 18px; | |||
| top: 93px; | |||
| right: 24px; | |||
| cursor: pointer; | |||
| background-color: ${selectedTheme.colors.primaryBackgroundColor} !important; | |||
| & div { | |||
| @@ -14,7 +14,7 @@ const Profile = () => { | |||
| const userId = useSelector(selectUserId); | |||
| const dispatch = useDispatch(); | |||
| const routeMatch = useRouteMatch(); | |||
| const idProfile = routeMatch.params.idProfile; | |||
| const idProfile = useMemo(() => routeMatch.params.idProfile, [routeMatch]); | |||
| useEffect(() => { | |||
| if (idProfile?.length > 0) { | |||
| dispatch(fetchProfile(idProfile)); | |||
| @@ -22,11 +22,8 @@ const Profile = () => { | |||
| } | |||
| }, [idProfile]); | |||
| const isMyProfile = useMemo(() => { | |||
| if (userId === idProfile) { | |||
| return true; | |||
| } | |||
| return false; | |||
| }, [userId, idProfile]); | |||
| return userId === routeMatch.params.idProfile; | |||
| }, [userId, routeMatch]); | |||
| return ( | |||
| <ProfileContainer> | |||
| <Header /> | |||
| @@ -20,9 +20,11 @@ export const OffersContainer = styled(Box)` | |||
| `; | |||
| export const OffersScroller = styled(HorizontalScroller)` | |||
| height: 373px; | |||
| width: 100%; | |||
| margin-left: 0; | |||
| & div { | |||
| margin-left: 0; | |||
| margin-right: 0; | |||
| gap: 18px; | |||
| } | |||
| `; | |||
| @@ -0,0 +1,58 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| DownArrowIcon, | |||
| HeaderSelect, | |||
| SelectOption, | |||
| } from "./ReviewsSorting.styled"; | |||
| import { useState } from "react"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { selectSelectedReviews } from "../../../store/selectors/reviewSelector"; | |||
| import { sortReviews } from "../../../util/helpers/reviewsHelper"; | |||
| import { reviewSortEnum } from "../../../enums/reviewEnum"; | |||
| import { setReviews } from "../../../store/actions/review/reviewActions"; | |||
| const ReviewsSorting = () => { | |||
| const reviews = useSelector(selectSelectedReviews); | |||
| const dispatch = useDispatch(); | |||
| const [value, setValue] = useState(); | |||
| const changeValue = (event) => { | |||
| dispatch( | |||
| setReviews( | |||
| sortReviews( | |||
| reviews, | |||
| event.target.value.value === reviewSortEnum.POSITIVE.value | |||
| ) | |||
| ) | |||
| ); | |||
| setValue(event.target.value); | |||
| }; | |||
| return ( | |||
| <HeaderSelect | |||
| value={value || reviewSortEnum.INITIAL} | |||
| IconComponent={DownArrowIcon} | |||
| onChange={changeValue} | |||
| > | |||
| <SelectOption style={{ display: "none" }} value={reviewSortEnum.INITIAL}> | |||
| {reviewSortEnum.INITIAL.mainText} | |||
| </SelectOption> | |||
| {Object.keys(reviewSortEnum).map((property) => { | |||
| if (reviewSortEnum[property].value === 0) return; | |||
| return ( | |||
| <SelectOption | |||
| value={reviewSortEnum[property]} | |||
| key={reviewSortEnum[property].value} | |||
| > | |||
| {reviewSortEnum[property].mainText} | |||
| </SelectOption> | |||
| ); | |||
| })} | |||
| </HeaderSelect> | |||
| ); | |||
| }; | |||
| ReviewsSorting.propTypes = { | |||
| children: PropTypes.node, | |||
| }; | |||
| export default ReviewsSorting; | |||
| @@ -0,0 +1,35 @@ | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../themes"; | |||
| import Option from "../../Select/Option/Option"; | |||
| import Select from "../../Select/Select"; | |||
| import { ReactComponent as DownArrow } from "../../../assets/images/svg/down-arrow.svg"; | |||
| export const HeaderSelect = styled(Select)` | |||
| width: 133px; | |||
| height: 35px; | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| margin-top: 3px; | |||
| font-weight: 400; | |||
| position: relative; | |||
| left: -5px; | |||
| background-color: white; | |||
| & div:first-child { | |||
| padding-right: 0 !important; | |||
| padding-left: 8px; | |||
| } | |||
| @media (max-width: 650px) { | |||
| width: 144px; | |||
| height: 30px; | |||
| font-size: 14px; | |||
| } | |||
| `; | |||
| export const SelectOption = styled(Option)` | |||
| @media (max-width: 600px) { | |||
| height: 36px !important; | |||
| min-height: 35px; | |||
| margin: 4px; | |||
| font-size: 14px; | |||
| } | |||
| `; | |||
| export const DownArrowIcon = styled(DownArrow)``; | |||
| @@ -5,11 +5,6 @@ import { | |||
| ReviewsBox, | |||
| ReviewsHeader, | |||
| ReviewsHeaderTitle, | |||
| ReviewSortContainer, | |||
| ReviewSortDescription, | |||
| ReviewSortIcon, | |||
| ReviewSortOption, | |||
| ReviewSortOptionContainer, | |||
| ReviewsTitle, | |||
| } from "./UserReviews.styled"; | |||
| @@ -21,11 +16,16 @@ import { useDispatch, useSelector } from "react-redux"; | |||
| import { selectOffer } from "../../store/selectors/offersSelectors"; | |||
| import { selectSelectedReviews } from "../../store/selectors/reviewSelector"; | |||
| import { useRouteMatch } from "react-router-dom"; | |||
| import { fetchReviews } from "../../store/actions/review/reviewActions"; | |||
| import { | |||
| fetchReviews, | |||
| setReviews, | |||
| } from "../../store/actions/review/reviewActions"; | |||
| import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors"; | |||
| import SkeletonUserReviews from "./SkeletonUserReviews/SkeletonUserReviews"; | |||
| import { ONE_OFFER_SCOPE } from "../../store/actions/offers/offersActionConstants"; | |||
| import { REVIEW_GET_SCOPE } from "../../store/actions/review/reviewActionConstants"; | |||
| import ReviewsSorting from "./ReviewsSorting/ReviewsSorting"; | |||
| import { sortReviews } from "../../util/helpers/reviewsHelper"; | |||
| const UserReviews = (props) => { | |||
| const { t } = useTranslation(); | |||
| @@ -35,7 +35,9 @@ const UserReviews = (props) => { | |||
| const dispatch = useDispatch(); | |||
| const isLoadingReview = useSelector( | |||
| selectIsLoadingByActionType(props.isProfileReviews ? REVIEW_GET_SCOPE : ONE_OFFER_SCOPE) | |||
| selectIsLoadingByActionType( | |||
| props.isProfileReviews ? REVIEW_GET_SCOPE : ONE_OFFER_SCOPE | |||
| ) | |||
| ); | |||
| useEffect(() => { | |||
| @@ -52,14 +54,23 @@ const UserReviews = (props) => { | |||
| return [...reviews]; | |||
| } | |||
| if (offer?.companyData?.lastThreeReviews) { | |||
| return [...offer?.companyData.lastThreeReviews]; | |||
| // Making array of reviews in same order(sorting) so when comparing | |||
| // them, I can make valid condition | |||
| if ( | |||
| JSON.stringify(sortReviews(reviews)) !== | |||
| JSON.stringify(sortReviews(offer?.companyData?.lastThreeReviews)) | |||
| ) { | |||
| dispatch(setReviews([...offer?.companyData.lastThreeReviews])); | |||
| } | |||
| return [...reviews]; | |||
| } | |||
| return []; | |||
| }, [props.profileReviews, offer, props.isProfileReviews, reviews]); | |||
| return ( | |||
| <> | |||
| {isLoadingReview || isLoadingReview === undefined ? ( | |||
| {!props.givingReview && | |||
| (isLoadingReview || isLoadingReview === undefined) ? ( | |||
| <SkeletonUserReviews /> | |||
| ) : ( | |||
| <ReviewsBox | |||
| @@ -82,16 +93,10 @@ const UserReviews = (props) => { | |||
| /> | |||
| <ReviewsTitle>{t("reviews.rates")}</ReviewsTitle> | |||
| </ReviewsHeaderTitle> | |||
| <ReviewSortContainer> | |||
| <ReviewSortDescription>Sortiraj po: </ReviewSortDescription> | |||
| <ReviewSortOptionContainer> | |||
| <ReviewSortOption>Pozitivne</ReviewSortOption> | |||
| <ReviewSortIcon /> | |||
| </ReviewSortOptionContainer> | |||
| </ReviewSortContainer> | |||
| <ReviewsSorting /> | |||
| </ReviewsHeader> | |||
| )} | |||
| <ReviewList> | |||
| <ReviewList isProfileReviews={props.isProfileReviews}> | |||
| {lastThreeReviews?.length > 0 ? ( | |||
| lastThreeReviews?.map((review, index) => ( | |||
| <UserReviewsCard | |||
| @@ -51,13 +51,14 @@ export const ReviewsTitle = styled(Typography)` | |||
| export const ReviewList = styled(List)` | |||
| background: white; | |||
| padding: 2rem; | |||
| padding-top: 6px; | |||
| padding: 0 18px; | |||
| border-radius: 4px 0 0 4px; | |||
| padding-right: 0.4rem; | |||
| height: 100%; | |||
| width: 100%; | |||
| border: 1px solid ${selectedTheme.colors.borderNormal}; | |||
| overflow-y: auto; | |||
| max-height: ${(props) => (props.isProfileReviews ? "70vh" : "43vh")}; | |||
| /* overflow-y: auto; */ | |||
| &::-webkit-scrollbar { | |||
| @@ -1,20 +1,39 @@ | |||
| export const reviewEnum = { | |||
| YES: { | |||
| YES: { | |||
| value: 1, | |||
| mainText: "Da", | |||
| // Unsuccessful swap | |||
| backendText: "succeeded", | |||
| // Not good communication | |||
| backendTextSecond: "yes", | |||
| }, | |||
| NO: { | |||
| value: 2, | |||
| mainText: "Ne", | |||
| // Unsuccessful swap | |||
| backendText: "failed", | |||
| // Not good communication | |||
| backendTextSecond: "no", | |||
| }, | |||
| NOT_BAD: { | |||
| value: 3, | |||
| mainText: "Može bolje", | |||
| backendText: "could be better", | |||
| }, | |||
| }; | |||
| export const reviewSortEnum = { | |||
| INITIAL: { | |||
| value: 0, | |||
| mainText: "Sortiraj po", | |||
| queryString: "" | |||
| }, | |||
| POSITIVE: { | |||
| value: 1, | |||
| mainText: "Da", | |||
| backendText: "succeeded" | |||
| mainText: "Pozitivne", | |||
| }, | |||
| NO: { | |||
| NEGATIVE: { | |||
| value: 2, | |||
| mainText: "Ne", | |||
| // Unsuccessful swap | |||
| backendText: "failed", | |||
| // Not good communication | |||
| backendTextSecond: "no" | |||
| mainText: "Negativne", | |||
| }, | |||
| NOT_BAD: { | |||
| value: 3, | |||
| mainText: "Može bolje", | |||
| backendText: "could be better" | |||
| } | |||
| } | |||
| } | |||
| @@ -173,6 +173,7 @@ export default { | |||
| product: "Proizvod", | |||
| descriptionLabel: "Opis:", | |||
| checkButtonLabel: "Pogledaj proizvod", | |||
| offers: "Objave" | |||
| }, | |||
| apiErrors: { | |||
| somethingWentWrong: "Greška sa serverom!", | |||
| @@ -207,6 +208,7 @@ export default { | |||
| rates: "Ocene kompanije", | |||
| finishedReviewTitle: "Hvala vam", | |||
| finishedReviewAltTitle: "na izdvojenom vremenu i datoj oceni!", | |||
| sortBy: "Sortiraj po", | |||
| }, | |||
| messages: { | |||
| headerTitle: "Moje Ćaskanje", | |||
| @@ -217,7 +219,7 @@ export default { | |||
| seeChats: "Pogledaj ćaskanje", | |||
| noMessagesToast: "Nemate ni jednu poruku!", | |||
| notAllowedChat: "Trampa za ovaj proizvod je završena.", | |||
| goBack: "Nazad na sve poruke" | |||
| goBack: "Nazad na sve poruke", | |||
| }, | |||
| editProfile: { | |||
| website: "Web Sajt*", | |||
| @@ -16,7 +16,7 @@ export const ItemDetailsLayoutContainer = styled(Container)` | |||
| } | |||
| @media (max-width: 600px) { | |||
| padding-left: 18px; | |||
| padding-right: ${(props) => (props.profile ? 0 : "18px")}; | |||
| padding-right: 18px; | |||
| } | |||
| `; | |||
| @@ -152,7 +152,8 @@ function* fetchMoreOffers(payload) { | |||
| function* createOffer(payload) { | |||
| try { | |||
| const offerData = payload.payload.values.offerData; | |||
| console.log(payload); | |||
| const offerData = payload.payload.offerData; | |||
| const formData = new FormData(); | |||
| formData.append("category[name]", offerData.category.name); | |||
| formData.append("condition", offerData.condition); | |||
| @@ -0,0 +1,53 @@ | |||
| import { reviewEnum } from "../../enums/reviewEnum"; | |||
| export const sortReviews = (reviews, positive = false) => { | |||
| let newReviews; | |||
| if (positive) { | |||
| newReviews = [ | |||
| ...reviews.filter( | |||
| (review) => | |||
| review.succeeded === reviewEnum.YES.backendText && | |||
| review.communication === reviewEnum.YES.backendTextSecond | |||
| ), | |||
| ...reviews.filter( | |||
| (review) => | |||
| review.succeeded === reviewEnum.YES.backendText && | |||
| review.communication === reviewEnum.NOT_BAD.backendText | |||
| ), | |||
| ...reviews.filter( | |||
| (review) => | |||
| review.succeeded === reviewEnum.YES.backendText && | |||
| review.communication === reviewEnum.NO.backendTextSecond | |||
| ), | |||
| ...reviews.filter( | |||
| (review) => review.succeeded === reviewEnum.NO.backendText | |||
| ), | |||
| ]; | |||
| } else { | |||
| newReviews = [ | |||
| ...reviews.filter( | |||
| // 4 | |||
| (review) => review.succeeded === reviewEnum.NO.backendText | |||
| ), | |||
| ...reviews.filter( | |||
| // 3 | |||
| (review) => | |||
| review.succeeded === reviewEnum.YES.backendText && | |||
| review.communication === reviewEnum.NO.backendTextSecond | |||
| ), | |||
| ...reviews.filter( | |||
| // 2 | |||
| (review) => | |||
| review.succeeded === reviewEnum.YES.backendText && | |||
| review.communication === reviewEnum.NOT_BAD.backendText | |||
| ), | |||
| ...reviews.filter( | |||
| // prvo | |||
| (review) => | |||
| review.succeeded === reviewEnum.YES.backendText && | |||
| review.communication === reviewEnum.YES.backendTextSecond | |||
| ), | |||
| ]; | |||
| } | |||
| return newReviews; | |||
| }; | |||