| @@ -42,7 +42,13 @@ const ChatCard = (props) => { | |||
| <Col> | |||
| <UserImgWrapper> | |||
| {/* <UserImage src={chat?.interlocutorData?.image} /> */} | |||
| <UserImage src={getImageUrl(chat?.interlocutorData?.image, variants.chatCard, isMobile)} /> | |||
| <UserImage | |||
| src={getImageUrl( | |||
| chat?.interlocutorData?.image, | |||
| variants.chatCard, | |||
| isMobile | |||
| )} | |||
| /> | |||
| </UserImgWrapper> | |||
| <ChatInfo> | |||
| @@ -16,14 +16,16 @@ const ChatCommands = (props) => { | |||
| const [showPhonePopover, setShowPhonePopover] = useState(false); | |||
| const [phonePopoverAnchorEl, setPhonePopoverAnchorEl] = useState(null); | |||
| const togglePhonePopover = (event) => { | |||
| setShowPhonePopover((prevState) => !prevState); | |||
| setPhonePopoverAnchorEl((prevState) => | |||
| prevState ? null : event.currentTarget | |||
| ); | |||
| }; | |||
| return ( | |||
| <Commands> | |||
| <PhoneIconContainer | |||
| onClick={(event) => { | |||
| setShowPhonePopover(true); | |||
| setPhonePopoverAnchorEl(event.currentTarget); | |||
| }} | |||
| > | |||
| <PhoneIconContainer onClick={togglePhonePopover}> | |||
| <PhoneIcon /> | |||
| </PhoneIconContainer> | |||
| <CheckButton | |||
| @@ -39,10 +41,7 @@ const ChatCommands = (props) => { | |||
| anchorEl={phonePopoverAnchorEl} | |||
| open={showPhonePopover} | |||
| anchorRight | |||
| onClose={() => { | |||
| setShowPhonePopover(false); | |||
| setPhonePopoverAnchorEl(null); | |||
| }} | |||
| onClose={togglePhonePopover} | |||
| content={<PhonePopover />} | |||
| /> | |||
| </Commands> | |||
| @@ -14,9 +14,9 @@ import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter"; | |||
| import useIsMobile from "../../../../hooks/useIsMobile"; | |||
| const LittleOfferDetails = (props) => { | |||
| const chat = props.chat; | |||
| const { t } = useTranslation(); | |||
| const { isMobile } = useIsMobile(); | |||
| const chat = props.chat; | |||
| return ( | |||
| <Col mobileDisappear> | |||
| <ChatOffer> | |||
| @@ -1,21 +1,26 @@ | |||
| import React from 'react' | |||
| import PropTypes from 'prop-types' | |||
| import { LocationContainer, LocationIcon, LocationIconContainer, XSText } from './OfferLocation.styled'; | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| LocationContainer, | |||
| LocationIcon, | |||
| LocationIconContainer, | |||
| XSText, | |||
| } from "./OfferLocation.styled"; | |||
| const OfferLocation = (props) => { | |||
| const chat = props.chat; | |||
| const chat = props.chat; | |||
| return ( | |||
| <LocationContainer> | |||
| <LocationIconContainer> | |||
| <LocationIcon /> | |||
| </LocationIconContainer> | |||
| <XSText>{chat?.interlocutorData?.location}</XSText> | |||
| </LocationContainer> | |||
| ) | |||
| } | |||
| <LocationIconContainer> | |||
| <LocationIcon /> | |||
| </LocationIconContainer> | |||
| <XSText>{chat?.interlocutorData?.location}</XSText> | |||
| </LocationContainer> | |||
| ); | |||
| }; | |||
| OfferLocation.propTypes = { | |||
| chat: PropTypes.any, | |||
| } | |||
| chat: PropTypes.any, | |||
| }; | |||
| export default OfferLocation | |||
| export default OfferLocation; | |||
| @@ -89,7 +89,6 @@ const CreateOffer = ({ closeCreateOfferModal, editOffer, offer }) => { | |||
| } | |||
| closeCreateOfferModal(false); | |||
| }; | |||
| const goStepBack = (stepNumber) => { | |||
| setCurrentStep(stepNumber); | |||
| const { | |||
| @@ -50,13 +50,11 @@ const SecondPartCreateOffer = (props) => { | |||
| useEffect(() => { | |||
| setImages((prevState) => { | |||
| let editedImages = [...prevState]; | |||
| if (props.offer !== undefined && props.offer.images.length === 1) { | |||
| if (props.offer !== undefined && props.offer.images.length !== 0) { | |||
| editedImages[0] = props.offer.images[0]; | |||
| } | |||
| if (props.offer !== undefined && props.offer.images.length === 2) { | |||
| editedImages[0] = props.offer.images[0]; | |||
| editedImages[1] = props.offer.images[1]; | |||
| props.offer.images.forEach((oldImage, index) => { | |||
| editedImages[index] = oldImage | |||
| }) | |||
| } | |||
| return [...editedImages]; | |||
| @@ -38,6 +38,7 @@ const ThirdPartCreateOffer = (props) => { | |||
| showBarterButton={false} | |||
| showPublishButton={false} | |||
| showExchangeButton={false} | |||
| createOffer | |||
| hideViews | |||
| /> | |||
| </CreateOfferFormContainer> | |||
| @@ -98,6 +98,7 @@ const ItemDetailsCard = (props) => { | |||
| </OfferInfo> | |||
| <OfferDetails | |||
| offer={offer} | |||
| createOffer={props.createOffer} | |||
| showExchangeButton={props.showExchangeButton} | |||
| showPublishButton={props.showPublishButton} | |||
| singleOffer={props.singleOffer} | |||
| @@ -156,6 +157,7 @@ ItemDetailsCard.propTypes = { | |||
| className: PropTypes.string, | |||
| singleOffer: PropTypes.bool, | |||
| isMyOffer: PropTypes.bool, | |||
| createOffer: PropTypes.bool, | |||
| }; | |||
| ItemDetailsCard.defaultProps = { | |||
| halfwidth: false, | |||
| @@ -17,12 +17,39 @@ import useScreenDimensions from "../../../../hooks/useScreenDimensions"; | |||
| import { formatDateLocale } from "../../../../util/helpers/dateHelpers"; | |||
| import useIsMobile from "../../../../hooks/useIsMobile"; | |||
| import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter"; | |||
| import { useEffect } from "react"; | |||
| import { useState } from "react"; | |||
| const OfferDetails = (props) => { | |||
| const offer = props.offer; | |||
| const { t } = useTranslation(); | |||
| const dimension = useScreenDimensions(); | |||
| const { isMobile } = useIsMobile(); | |||
| const [images, setImages] = useState([]); | |||
| useEffect(() => { | |||
| if (props?.offer?.offer?.images) { | |||
| props.offer.offer.images.map((file) => { | |||
| if (file) { | |||
| if (typeof file !== "string") { | |||
| var reader = new FileReader(); | |||
| reader.readAsDataURL(file); | |||
| reader.onload = function () { | |||
| setImages((prevImages) => [...prevImages, reader.result]); | |||
| }; | |||
| reader.onerror = function (error) { | |||
| console.log("Error: ", error); | |||
| }; | |||
| } else { | |||
| setImages((prevImages) => [ | |||
| ...prevImages, | |||
| getImageUrl(file, variants.offerCard, isMobile), | |||
| ]); | |||
| } | |||
| } | |||
| }); | |||
| } | |||
| }, [props?.offer?.offer?.images]); | |||
| const date = formatDateLocale(new Date(offer?.offer?._created)); | |||
| return ( | |||
| <Details | |||
| @@ -32,18 +59,29 @@ const OfferDetails = (props) => { | |||
| > | |||
| {dimension.width < 600 || !props.singleOffer ? ( | |||
| <ScrollerHorizontal> | |||
| {offer?.offer?.images?.map((item) => ( | |||
| <OfferImage | |||
| src={getImageUrl(item, variants.offerCard, isMobile)} | |||
| key={item} | |||
| /> | |||
| ))} | |||
| {props?.offer?.offer?.images.map((item, index) => { | |||
| if (!item) return; | |||
| return ( | |||
| <OfferImage | |||
| src={ | |||
| props.createOffer | |||
| ? images[index] | |||
| : getImageUrl(item, variants.offerCard, isMobile) | |||
| } | |||
| key={item} | |||
| /> | |||
| ); | |||
| })} | |||
| </ScrollerHorizontal> | |||
| ) : ( | |||
| <ScrollerVertical> | |||
| {offer?.offer?.images?.map((item) => ( | |||
| {props?.offer?.offer?.images.map((item, index) => ( | |||
| <OfferImage | |||
| src={getImageUrl(item, variants.offerCard, isMobile)} | |||
| src={ | |||
| props.createOffer | |||
| ? images[index] | |||
| : getImageUrl(item, variants.offerCard, isMobile) | |||
| } | |||
| key={item} | |||
| /> | |||
| ))} | |||
| @@ -72,6 +110,7 @@ OfferDetails.propTypes = { | |||
| showExchangeButton: PropTypes.bool, | |||
| showPublishButton: PropTypes.bool, | |||
| singleOffer: PropTypes.bool, | |||
| createOffer: PropTypes.bool, | |||
| }; | |||
| export default OfferDetails; | |||
| @@ -1,22 +1,30 @@ | |||
| import React from 'react' | |||
| import PropTypes from 'prop-types' | |||
| import { ReviewOfferContainer, ReviewOfferDescription, ReviewOfferDetails, ReviewOfferImage, ReviewOfferTitle } from './ReviewOffer.styled' | |||
| const ReviewOffer = () => { | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| ReviewOfferContainer, | |||
| ReviewOfferDescription, | |||
| ReviewOfferDetails, | |||
| ReviewOfferImage, | |||
| ReviewOfferTitle, | |||
| } from "./ReviewOffer.styled"; | |||
| import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter"; | |||
| const ReviewOffer = (props) => { | |||
| return ( | |||
| <ReviewOfferContainer> | |||
| <ReviewOfferImage src="https://diligentproduction.s3.eu-central-1.amazonaws.com/trampa/7bbd641a-8d9c-4a85-be0e-4263ff6fc673.jpg"/> | |||
| <ReviewOfferDetails> | |||
| <ReviewOfferDescription>Proizvod: </ReviewOfferDescription> | |||
| <ReviewOfferTitle>Neki proizvod</ReviewOfferTitle> | |||
| </ReviewOfferDetails> | |||
| <ReviewOfferImage src={getImageUrl(props.image, variants.reviewCard)} /> | |||
| <ReviewOfferDetails> | |||
| <ReviewOfferDescription>Proizvod: </ReviewOfferDescription> | |||
| <ReviewOfferTitle>{props.name}</ReviewOfferTitle> | |||
| </ReviewOfferDetails> | |||
| </ReviewOfferContainer> | |||
| ) | |||
| } | |||
| ); | |||
| }; | |||
| ReviewOffer.propTypes = { | |||
| children: PropTypes.node, | |||
| } | |||
| children: PropTypes.node, | |||
| name: PropTypes.string, | |||
| image: PropTypes.string, | |||
| }; | |||
| export default ReviewOffer | |||
| export default ReviewOffer; | |||
| @@ -26,7 +26,6 @@ import { reviewEnum } from "../../../enums/reviewEnum"; | |||
| const UserReviewsCard = (props) => { | |||
| const { t } = useTranslation(); | |||
| const { isMobile } = useIsMobile(); | |||
| console.log(props); | |||
| const review = useMemo(() => { | |||
| if (props.givingReview) { | |||
| @@ -34,7 +33,6 @@ const UserReviewsCard = (props) => { | |||
| ...props.review, | |||
| }; | |||
| } | |||
| console.log(props.review); | |||
| let isSuccessfulSwap = "DA"; | |||
| if ( | |||
| props.review.succeeded === "failed" || | |||
| @@ -53,13 +51,16 @@ const UserReviewsCard = (props) => { | |||
| ) | |||
| isGoodCommunication = "NE"; | |||
| return { | |||
| name: props.review.companyName, | |||
| image: props.review.image, | |||
| name: props.review.userWhoGaveReview.name, | |||
| image: props.review.userWhoGaveReview.image, | |||
| isGoodCommunication, | |||
| isSuccessfulSwap, | |||
| quote: props?.review?.message, | |||
| offerName: props.review.offer.name, | |||
| offerImage: props.review.offer.image, | |||
| }; | |||
| }, [props.review]); | |||
| console.log(props); | |||
| return ( | |||
| <ReviewContainer key={review?.image}> | |||
| @@ -83,7 +84,8 @@ const UserReviewsCard = (props) => { | |||
| sx={{ pl: 2, py: 2 }} | |||
| > | |||
| <ThumbBox item> | |||
| {review.isSuccessfulSwap === reviewEnum.YES.mainText ? ( | |||
| {review.isSuccessfulSwap.toLowerCase() === | |||
| reviewEnum.YES.mainText.toLowerCase() ? ( | |||
| <ThumbUp color="success" /> | |||
| ) : ( | |||
| <ThumbDown color="error" /> | |||
| @@ -107,7 +109,7 @@ const UserReviewsCard = (props) => { | |||
| </ReviewDetailsValue> | |||
| </ReviewDetailsText> | |||
| </ReviewDetails> | |||
| <ReviewOffer /> | |||
| <ReviewOffer name={review.offerName} image={review.offerImage}/> | |||
| </ReviewContainer> | |||
| ); | |||
| }; | |||
| @@ -8,6 +8,7 @@ export const ReviewsBox = styled(Box)` | |||
| width: 100%; | |||
| height: calc(100% - 90px); | |||
| max-height: 100vh; | |||
| padding-bottom: 20px; | |||
| @media (max-width: 1200px) { | |||
| padding: 0; | |||
| } | |||
| @@ -129,4 +130,5 @@ export const ProfileName = styled(Typography)` | |||
| font-family: "DM Sans"; | |||
| ` | |||
| export const ReviewContainer = styled(Box)` | |||
| padding-bottom: 20px; | |||
| ` | |||
| @@ -27,7 +27,6 @@ const FirstStepCreateReview = (props) => { | |||
| const { isMobile } = useIsMobile(); | |||
| const { t } = useTranslation(); | |||
| const handleSubmit = (values) => { | |||
| console.log(values) | |||
| props.goToNextStep(values); | |||
| }; | |||
| @@ -7,29 +7,35 @@ import { | |||
| import { CreateReviewTitle, NextButton } from "../CreateReview.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { useSelector } from "react-redux"; | |||
| import { selectMineProfile } from "../../../store/selectors/profileSelectors"; | |||
| const SecondStepCreateReview = (props) => { | |||
| const {t} = useTranslation(); | |||
| const { t } = useTranslation(); | |||
| const mineProfile = useSelector(selectMineProfile); | |||
| console.log(mineProfile); | |||
| const goToNextStep = () => { | |||
| props.goToNextStep(); | |||
| } | |||
| console.log(props.review); | |||
| }; | |||
| console.log(props); | |||
| return ( | |||
| <SecondStepCreateReviewContainer> | |||
| <CreateReviewTitle>{t("reviews.modalTitle")}</CreateReviewTitle> | |||
| <ReviewCard | |||
| givingReview | |||
| profileReviews={ | |||
| [{ | |||
| name: props.interlocutor?.name, | |||
| image: props.interlocutor?.image, | |||
| 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, | |||
| }] | |||
| } | |||
| }, | |||
| ]} | |||
| /> | |||
| <NextButton | |||
| variant="contained" | |||
| @@ -79,8 +79,7 @@ const DirectChat = () => { | |||
| addMesageListener((data) => { | |||
| if ( | |||
| [...allChats].find((item) => { | |||
| console.log("item.chat._id", item.chat._id); | |||
| console.log("data.chatId", data.chatId); | |||
| return item.chat._id === data.chatId; | |||
| }) | |||
| ) { | |||
| @@ -16,7 +16,6 @@ import { selectIsLoadingByActionType } from "../../../store/selectors/loadingSel | |||
| import { CHAT_SCOPE } from "../../../store/actions/chat/chatActionConstants"; | |||
| const DirectChatContent = (props) => { | |||
| const messages = props?.chat?.chat?.messages; | |||
| const userId = useSelector(selectUserId); | |||
| const myProfileImage = useSelector(selectMineProfilePicture); | |||
| const messagesRef = useRef(null); | |||
| @@ -24,14 +23,18 @@ const DirectChatContent = (props) => { | |||
| const isLoadingChatContent = useSelector( | |||
| selectIsLoadingByActionType(CHAT_SCOPE) | |||
| ); | |||
| const messages = props?.chat?.chat?.messages; | |||
| useEffect(() => { | |||
| messagesRef.current?.scrollTo({ | |||
| top: messagesRef.current.scrollHeight, | |||
| behaviour: "smooth", | |||
| }); | |||
| }, [messages]); | |||
| const handleRefresh = () => { | |||
| props.refreshChat(); | |||
| }; | |||
| useEffect(() => { | |||
| // const offsetBottom = | |||
| // messagesRef.current?.offsetTop + messagesRef.current?.offsetHeight; | |||
| messagesRef.current?.scrollTo({ top: messagesRef.current.scrollHeight, behaviour: "smooth" }); | |||
| }, [messages]); | |||
| return ( | |||
| <> | |||
| {isLoadingChatContent || isLoadingChatContent === undefined ? ( | |||
| @@ -58,7 +61,6 @@ const DirectChatContent = (props) => { | |||
| </MessagesList> | |||
| <DirectChatNewMessage | |||
| chat={props?.chat} | |||
| chatId={props?.chat?.chat?._id} | |||
| refreshChat={handleRefresh} | |||
| interlucator={props.interlucator} | |||
| /> | |||
| @@ -23,14 +23,15 @@ const DirectChatContentHeader = (props) => { | |||
| const { isMobile } = useIsMobile(); | |||
| const togglePhonePopover = (event) => { | |||
| setShowPhonePopover(true); | |||
| setPhonePopoverAnchorEl(event.currentTarget); | |||
| setShowPhonePopover((prevState) => !prevState); | |||
| setPhonePopoverAnchorEl((prevState) => | |||
| prevState ? null : event.currentTarget | |||
| ); | |||
| }; | |||
| return ( | |||
| <DirectChatContentHeaderContainer> | |||
| <DirectChatContentHeaderFlexContainer> | |||
| {/* <ProfileImage src={props?.interlucator?.image} /> */} | |||
| <ProfileImage | |||
| src={getImageUrl( | |||
| props?.interlucator?.image, | |||
| @@ -57,10 +58,7 @@ const DirectChatContentHeader = (props) => { | |||
| anchorEl={phonePopoverAnchorEl} | |||
| open={showPhonePopover} | |||
| anchorRight | |||
| onClose={() => { | |||
| setShowPhonePopover(false); | |||
| setPhonePopoverAnchorEl(null); | |||
| }} | |||
| onClose={togglePhonePopover} | |||
| content={<PhonePopover />} | |||
| /> | |||
| </DirectChatContentHeaderContainer> | |||
| @@ -17,28 +17,23 @@ const DirectChatHeader = (props) => { | |||
| const dispatch = useDispatch(); | |||
| const chat = useSelector(selectSelectedChat); | |||
| const [showReviewModal, setShowReviewModal] = useState(false); | |||
| useEffect(() => { | |||
| if (chat?.chat?.exchangeId) { | |||
| refetchExchange(); | |||
| } | |||
| if (chat?.chat?.exchangeId) refetchExchange(); | |||
| }, [chat]); | |||
| const isDisabledReviews = useMemo(() => { | |||
| if (!exchange.valid) return true; | |||
| if (exchange.seller?.userId === userId) { | |||
| if (exchange.seller?.givenReview) return true; | |||
| } | |||
| if (exchange.buyer?.userId === userId) { | |||
| if (exchange.buyer?.givenReview) return true; | |||
| } | |||
| if (exchange.seller?.userId === userId && exchange.seller?.givenReview) | |||
| return true; | |||
| if (exchange.buyer?.userId === userId && exchange.buyer?.givenReview) | |||
| return true; | |||
| return false; | |||
| }, [exchange, userId]); | |||
| useEffect(() => { | |||
| if (showReviewModal) { | |||
| document.body.style.overflow = "hidden"; | |||
| } else { | |||
| document.body.style.overflow = "auto"; | |||
| } | |||
| if (showReviewModal) document.body.style.overflow = "hidden"; | |||
| else document.body.style.overflow = "auto"; | |||
| }, [showReviewModal]); | |||
| const makeReview = () => { | |||
| @@ -8,24 +8,16 @@ import { | |||
| import { useTranslation } from "react-i18next"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { useDispatch } from "react-redux"; | |||
| // import { | |||
| // fetchChats, | |||
| // startNewChat, | |||
| // } from "../../../store/actions/chat/chatActions"; | |||
| // import { useHistory, useLocation } from "react-router-dom"; | |||
| import { sendMessage } from "../../../socket/socket"; | |||
| import { useSelector } from "react-redux"; | |||
| import { selectUserId } from "../../../store/selectors/loginSelectors"; | |||
| import { | |||
| addNewMessage, | |||
| // fetchChats, | |||
| // fetchOneChat, | |||
| startNewChat, | |||
| } from "../../../store/actions/chat/chatActions"; | |||
| import { useHistory, useLocation } from "react-router-dom"; | |||
| import { selectExchange } from "../../../store/selectors/exchangeSelector"; | |||
| import { validateExchange } from "../../../store/actions/exchange/exchangeActions"; | |||
| // import { selectUserId } from "../../../store/selectors/loginSelectors"; | |||
| const DirectChatNewMessage = (props) => { | |||
| const [typedValue, setTypedValue] = useState(""); | |||
| @@ -35,31 +27,11 @@ const DirectChatNewMessage = (props) => { | |||
| const { t } = useTranslation(); | |||
| const location = useLocation(); | |||
| const history = useHistory(); | |||
| // const handleApiResponseSuccess = () => { | |||
| // props.refreshChat(); | |||
| // }; | |||
| const userId = useSelector(selectUserId); | |||
| // useEffect(() => { | |||
| // if (props.chatId) { | |||
| // dispatch(fetchOneChat(props.chatId)); | |||
| // console.log("fetchuje se") | |||
| // } | |||
| // }, [props.chatId]); | |||
| const handleSend = useCallback( | |||
| (newChatId = undefined) => { | |||
| // if (location.state?.offerId) { | |||
| // initiateNewChat(typedValue); | |||
| // } else { | |||
| // dispatch( | |||
| // sendMessage({ | |||
| // message: typedValue, | |||
| // chatId: props.chatId, | |||
| // handleApiResponseSuccess, | |||
| // }) | |||
| // ); | |||
| // } | |||
| if (props.chatId || newChatId) { | |||
| const chatId = props.chatId || newChatId; | |||
| if (props.chat?.chat?._id || newChatId) { | |||
| const chatId = props.chat?.chat?._id || newChatId; | |||
| sendMessage(chatId, userId, typedValue, props.interlucator.userId); | |||
| dispatch( | |||
| addNewMessage({ | |||
| @@ -71,7 +43,7 @@ const DirectChatNewMessage = (props) => { | |||
| }, | |||
| }) | |||
| ); | |||
| if (props.chatId) { | |||
| if (props.chat?.chat?._id) { | |||
| if (!exchange.valid && props.chat?.offer?.offer?.userId === userId) { | |||
| dispatch(validateExchange(exchange._id)); | |||
| } | |||
| @@ -79,44 +51,26 @@ const DirectChatNewMessage = (props) => { | |||
| } else { | |||
| initiateNewChat(typedValue); | |||
| } | |||
| // socket.emit("private_message", { | |||
| // chatId: props.chatId, | |||
| // receiverUserId: props.interlucator.userId, | |||
| // message: typedValue | |||
| // // message: { | |||
| // // userId: userId, | |||
| // // text: typedValue, | |||
| // // receiverUserId: props.interlucator.userId | |||
| // // } | |||
| // }) | |||
| setTypedValue(""); | |||
| }, | |||
| [typedValue, props.chatId, userId, props.interlucator.userId] | |||
| [typedValue, props.chat?.chat?._id, userId, props.interlucator.userId] | |||
| ); | |||
| const handleMessageSendSuccess = (newChatId) => { | |||
| history.replace(`${newChatId}`); | |||
| // dispatch(fetchChats()); | |||
| // sendMessage(newChatId, userId, typedValue, props.interlucator.userId); | |||
| // dispatch( | |||
| // addNewMessage({ | |||
| // _id: newChatId, | |||
| // message: { | |||
| // userId, | |||
| // text: typedValue, | |||
| // _created: new Date().toISOString(), | |||
| // }, | |||
| // }) | |||
| // ); | |||
| // handleSend(newChatId); | |||
| }; | |||
| useEffect(() => { | |||
| const listener = (event) => { | |||
| const listener = useCallback( | |||
| (event) => { | |||
| // Event key code 13 = ENTER key | |||
| if (event.keyCode === 13) handleSend(); | |||
| }; | |||
| }, | |||
| [isFocused, typedValue] | |||
| ); | |||
| useEffect(() => { | |||
| if (isFocused) window.addEventListener("keypress", listener); | |||
| return () => window.removeEventListener("keypress", listener); | |||
| }, [typedValue]); | |||
| }, [typedValue, isFocused]); | |||
| const initiateNewChat = (typedValue) => { | |||
| const offerId = location.state.offerId; | |||
| @@ -56,6 +56,7 @@ import { Drawer as HeaderDrawer } from "./Drawer/Drawer"; | |||
| import useSearch from "../../hooks/useOffers/useSearch"; | |||
| import { routeMatches } from "../../util/helpers/routeHelpers"; | |||
| import AboutHeader from "./AboutHeader/AboutHeader"; | |||
| import { useCallback } from "react"; | |||
| // import useQueryString from "../../hooks/useOffers/useQueryString"; | |||
| const Header = () => { | |||
| @@ -163,14 +164,16 @@ const Header = () => { | |||
| history.push(REGISTER_PAGE); | |||
| }; | |||
| let listener; | |||
| const handleFocusSearch = () => { | |||
| listener = (event) => { | |||
| let listener = useCallback( | |||
| (event) => { | |||
| if (event.keyCode === 13) { | |||
| event.preventDefault(); | |||
| handleSearch(searchRef.current.value); | |||
| } | |||
| }; | |||
| }, | |||
| [searchRef.current] | |||
| ); | |||
| const handleFocusSearch = () => { | |||
| searchRef.current.addEventListener("keyup", listener); | |||
| }; | |||
| const handleBlurSearch = () => { | |||
| @@ -19,10 +19,11 @@ const ImagePicker = (props) => { | |||
| const [image, setImage] = useState(""); | |||
| const [isEditing, setIsEditing] = useState(false); | |||
| // Check if file is type of string or File | |||
| useEffect(() => { | |||
| if (props.image) { | |||
| if (typeof props.image === 'string') { | |||
| setImage(getImageUrl(props.image, variants.offerCard)) | |||
| if (typeof props.image === "string") { | |||
| setImage(getImageUrl(props.image, variants.offerCard)); | |||
| } else { | |||
| handleImage(props.image); | |||
| } | |||
| @@ -45,14 +46,17 @@ const ImagePicker = (props) => { | |||
| window.addEventListener("click", listener); | |||
| return () => window.removeEventListener("click", listener); | |||
| }, []); | |||
| // Simulate click on file input | |||
| const handleChange = () => { | |||
| fileInputRef.current.value = ""; | |||
| fileInputRef.current.click(); | |||
| }; | |||
| // Reads image as both base64 and multipart type | |||
| const handleImage = (file) => { | |||
| let reader = new FileReader(); | |||
| reader.readAsDataURL(file); | |||
| // reader.readAsBinaryString(file); | |||
| reader.onload = () => { | |||
| if (props.setImage) props.setImage(file); | |||
| setImage(reader.result); | |||
| @@ -61,11 +65,13 @@ const ImagePicker = (props) => { | |||
| console.dir(error); | |||
| }; | |||
| }; | |||
| const handleDelete = () => { | |||
| if (props.deleteImage) props.deleteImage(); | |||
| setImage(""); | |||
| setIsEditing(false); | |||
| }; | |||
| return ( | |||
| <ImagePickerContainer | |||
| className={props.className} | |||
| @@ -18,12 +18,12 @@ const ItemDetails = (props) => { | |||
| const offer = useSelector(selectOffer); | |||
| const userId = useSelector(selectUserId); | |||
| const { t } = useTranslation(); | |||
| let isMyProfile = useMemo(() => { | |||
| if (offer?.offer?.userId?.toString() === userId?.toString()) { | |||
| return true; | |||
| } | |||
| if (offer?.offer?.userId?.toString() === userId?.toString()) return true; | |||
| return false; | |||
| }, [offer, userId]); | |||
| return ( | |||
| <ItemDetailsContainer> | |||
| <Header /> | |||
| @@ -51,10 +51,9 @@ const ItemDetailsHeaderCard = (props) => { | |||
| halfwidth={props.halfwidth ? 1 : 0} | |||
| > | |||
| <HeaderTop> | |||
| {/* <OfferImage src={offer?.companyData?.image} /> */} | |||
| <OfferImage | |||
| src={getImageUrl( | |||
| offer?.companyData?.image, | |||
| offer?.companyData?.image ? offer.companyData.image : "", | |||
| variants.profileImage, | |||
| isMobile | |||
| )} | |||
| @@ -13,8 +13,11 @@ const ErrorMessage = (props) => { | |||
| <ErrorText>{error}</ErrorText> | |||
| ) : formik.errors.email?.length > 0 && formik.touched.email ? ( | |||
| <ErrorText>{formik.errors.email}</ErrorText> | |||
| ) : formik.errors.password?.length > 0 && formik.touched.password && ( | |||
| <ErrorText>{formik.errors.password}</ErrorText> | |||
| ) : ( | |||
| formik.errors.password?.length > 0 && | |||
| formik.touched.password && ( | |||
| <ErrorText>{formik.errors.password}</ErrorText> | |||
| ) | |||
| )} | |||
| </> | |||
| ); | |||
| @@ -29,10 +29,12 @@ const Login = () => { | |||
| const passwordRef = useRef(null); | |||
| const [passwordReset, setPasswordReseted] = useState(false); | |||
| // Clear login errors when user firstly enters the page | |||
| useEffect(() => { | |||
| dispatch(clearLoginErrors()); | |||
| }, []); | |||
| // Api response callback function on success | |||
| const handleApiResponseSuccess = () => { | |||
| history.push({ | |||
| pathname: HOME_PAGE, | |||
| @@ -42,16 +44,19 @@ const Login = () => { | |||
| }); | |||
| }; | |||
| // Resets password to blank field when there is need to | |||
| useEffect(() => { | |||
| if (passwordReset) { | |||
| formik.setFieldValue("password", ""); | |||
| } | |||
| }, [passwordReset]); | |||
| // Api response callback function on error | |||
| const handleApiResponseError = () => { | |||
| setPasswordReseted(true); | |||
| }; | |||
| // Checks if form is valid, and send it if it is valid | |||
| const handleSubmitForm = (e) => { | |||
| e.preventDefault(); | |||
| if (!formik.isValid) { | |||
| @@ -83,6 +88,7 @@ const Login = () => { | |||
| enableReinitialize: true, | |||
| }); | |||
| // Clear API errors and replace them with validation errors | |||
| useEffect(() => { | |||
| if (error) { | |||
| if (formik.errors.email || formik.errors.password) { | |||
| @@ -1,62 +0,0 @@ | |||
| import React from 'react' | |||
| import {ReactComponent as DummyImage1 } from "../../assets/images/svg/dummyImages/offer-1.svg" | |||
| // import {ReactComponent as DummyImage2 } from "../../assets/images/svg/dummyImages/offer-2.svg" | |||
| // import {ReactComponent as DummyImage3 } from "../../assets/images/svg/dummyImages/offer-3.svg" | |||
| // import {ReactComponent as DummyImage4 } from "../../assets/images/svg/dummyImages/offer-4.svg" | |||
| export const packageEnum = { | |||
| package: "PACKAGE", | |||
| palette: "PALETTE", | |||
| piece: "PIECE" | |||
| } | |||
| export default [ | |||
| { | |||
| id: 0, | |||
| title: "Vino", | |||
| description: "Vinarija Aleksić osnovana je u Vranju 2006. godine, otvorivši time put oživljavanju vinogradarstva na jugu Srbije.", | |||
| category: "Hrana i pice", | |||
| author: "Vinarija Aleksic", | |||
| location: "Nis, Serbia", | |||
| image: <DummyImage1 />, | |||
| quantity: 20, | |||
| package: packageEnum.package, | |||
| numberOfViews: 18 | |||
| }, | |||
| { | |||
| id: 1, | |||
| title: "Vino", | |||
| description: "Vinarija Aleksić osnovana je u Vranju 2006. godine, otvorivši time put oživljavanju vinogradarstva na jugu Srbije.", | |||
| category: "Hrana i pice", | |||
| author: "Vinarija Aleksic", | |||
| location: "Nis, Serbia", | |||
| image: <DummyImage1 />, | |||
| quantity: 20, | |||
| package: packageEnum.package, | |||
| numberOfViews: 18 | |||
| }, | |||
| { | |||
| id: 2, | |||
| title: "Vino", | |||
| description: "Vinarija Aleksić osnovana je u Vranju 2006. godine, otvorivši time put oživljavanju vinogradarstva na jugu Srbije.", | |||
| category: "Hrana i pice", | |||
| author: "Vinarija Aleksic", | |||
| location: "Nis, Serbia", | |||
| image: <DummyImage1 />, | |||
| quantity: 20, | |||
| package: packageEnum.package, | |||
| numberOfViews: 18 | |||
| }, | |||
| { | |||
| id: 3, | |||
| title: "Vino", | |||
| description: "Vinarija Aleksić osnovana je u Vranju 2006. godine, otvorivši time put oživljavanju vinogradarstva na jugu Srbije.", | |||
| category: "Hrana i pice", | |||
| author: "Vinarija Aleksic", | |||
| location: "Nis, Serbia", | |||
| image: <DummyImage1 />, | |||
| quantity: 20, | |||
| package: packageEnum.package, | |||
| numberOfViews: 18 | |||
| } | |||
| ] | |||
| @@ -7,6 +7,7 @@ const HeadersMyOffers = (props) => { | |||
| const searchRef = useRef(null); | |||
| const {t} = useTranslation(); | |||
| let listener = useCallback((event) => { | |||
| // Event keycode 13 = ENTER keycode | |||
| if (event.keyCode === 13) { | |||
| event.preventDefault(); | |||
| handleSearch(); | |||
| @@ -16,6 +16,7 @@ const Offers = (props) => { | |||
| const offersRef = useRef(null); | |||
| const userId = useSelector(selectUserId); | |||
| const offers = props.offers; | |||
| // For skeleton screen | |||
| const arrayForMapping = Array.apply(null, Array(4)).map(() => {}); | |||
| const messageOneUser = (offer) => { | |||
| @@ -33,7 +33,6 @@ const Paging = (props) => { | |||
| <ArrowIcon side="left" /> | |||
| </Arrow> | |||
| {threeDotsBefore && ( | |||
| <React.Fragment> | |||
| <PageNumber onClick={() => props.changePage(1)}>1</PageNumber> | |||
| @@ -56,7 +55,7 @@ const Paging = (props) => { | |||
| </PageNumber> | |||
| ); | |||
| })} | |||
| {threeDotsAfter && ( | |||
| <React.Fragment> | |||
| {props.current + 3 !== pages && <ThreeDots>...</ThreeDots>} | |||
| @@ -6,12 +6,14 @@ import { | |||
| PhoneNumber, | |||
| PhonePopoverContainer, | |||
| } from "./PhonePopover.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const PhonePopover = () => { | |||
| const {t} = useTranslation(); | |||
| return ( | |||
| <PhonePopoverContainer> | |||
| <Arrow /> | |||
| <PhoneLabel>Broj telefona</PhoneLabel> | |||
| <PhoneLabel>{t("common.labelPhoneNumber")}</PhoneLabel> | |||
| <PhoneNumber>065/000-018</PhoneNumber> | |||
| </PhonePopoverContainer> | |||
| ); | |||
| @@ -16,9 +16,7 @@ const ProfileMini = () => { | |||
| const userId = useSelector(selectUserId); | |||
| const { t } = useTranslation(); | |||
| let isMyProfile = useMemo(() => { | |||
| if (offer?.offer?.userId?.toString() === userId?.toString()) { | |||
| return true; | |||
| } | |||
| if (offer?.offer?.userId?.toString() === userId?.toString()) return true; | |||
| return false; | |||
| }, [offer, userId]); | |||
| return ( | |||
| @@ -7,23 +7,20 @@ import { | |||
| StepLine, | |||
| } from "./StepProgress.styled"; | |||
| import { ReactComponent as Checkmark } from "../../assets/images/svg/checkmark.svg"; | |||
| import { useMemo } from "react"; | |||
| const StepProgress = (props) => { | |||
| const steps = []; | |||
| for (let i = 1; i <= props.numberOfSteps; i++) { | |||
| steps.push({ | |||
| done: i < props.current, | |||
| current: i === props.current, | |||
| }); | |||
| } | |||
| const functions = []; | |||
| steps.forEach((item, index) => { | |||
| if (props.functions[index]) { | |||
| functions.push(props.functions[index]); | |||
| } else { | |||
| functions.push(() => {}); | |||
| const steps = useMemo(() => { | |||
| let returnValue = []; | |||
| for (let i = 1; i <= props.numberOfSteps; i++) { | |||
| returnValue.push({ | |||
| done: i < props.current, | |||
| current: i === props.current, | |||
| }); | |||
| } | |||
| }); | |||
| return returnValue; | |||
| }, [props.current]); | |||
| return ( | |||
| <StepProgressContainer done> | |||
| {steps.map((item, index) => | |||
| @@ -35,9 +32,7 @@ const StepProgress = (props) => { | |||
| onClick={ | |||
| item.done | |||
| ? props.functions[index] | |||
| : () => { | |||
| console.log("neuspeh"); | |||
| } | |||
| : () => {} | |||
| } | |||
| > | |||
| {item.done ? <Checkmark /> : index + 1} | |||
| @@ -13,7 +13,7 @@ export const ReviewsBox = styled(Box)` | |||
| ? props.numOfReviews * 185 + 82 + "px" | |||
| : `calc(100% - 90px)`}; */ | |||
| /* max-height: 100vh; */ | |||
| min-width: 320px; | |||
| min-width: 290px; | |||
| /* @media (max-width: 1200px) { | |||
| padding: 0 50px; | |||
| } */ | |||
| @@ -52,7 +52,7 @@ const useOffers = () => { | |||
| useEffect(() => { | |||
| if (history.location.state?.logo) { | |||
| clear(); | |||
| clearFiltersAndApply(); | |||
| } | |||
| }, [history.location]); | |||
| @@ -20,6 +20,7 @@ export default { | |||
| labelFirm: "Ime Firme", | |||
| labelPIB: "PIB", | |||
| labelPhone: "Telefon", | |||
| labelPhoneNumber: "Broj telefona", | |||
| labelLocation: "Lokacija", | |||
| labelWebsite: "Adresa Websajta", | |||
| logout: "Odjavi se", | |||
| @@ -108,7 +108,8 @@ export const FooterText = styled(Typography)` | |||
| export const ProfileImagePicker = styled(ImagePicker)` | |||
| background: none; | |||
| margin: 36px; | |||
| /* margin: 36px; */ | |||
| margin-top: 36px; | |||
| background: ${selectedTheme.primaryIconBackgroundColor}; | |||
| background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='100' ry='100' stroke='%235A3984FF' stroke-width='2' stroke-dasharray='7%2c 12' stroke-dashoffset='44' stroke-linecap='square'/%3e%3c/svg%3e"); | |||
| border-radius: 100px; | |||
| @@ -6,6 +6,7 @@ const request = axios.create({ | |||
| // baseURL: "http://192.168.88.175:3005/", | |||
| // baseURL: "http://192.168.88.143:3001/", // DULE | |||
| baseURL: "https://trampa-api-test.dilig.net/", | |||
| // baseURL: "http://localhost:3001/", | |||
| headers: { | |||
| "Content-Type": "application/json", | |||
| }, | |||
| @@ -1,19 +1,19 @@ | |||
| import io from "socket.io-client"; | |||
| export const socket = io("https://trampa-api-test.dilig.net/", { | |||
| autoConnect: true, | |||
| transports: ["websocket"], | |||
| reconnectionAttempts: 5, | |||
| }); | |||
| export const socketInit = (userId) => { | |||
| if (socket.connected) socket.disconnect(); | |||
| socket.auth = { | |||
| userId, | |||
| }; | |||
| socket.connect(); | |||
| }; | |||
| export const sendMessage = (chatId, userId, text, receiverUserId) => { | |||
| console.log("CHATID: ", chatId); | |||
| socket.emit("private_message", { | |||
| chatId, | |||
| receiverUserId, | |||
| @@ -37,8 +37,6 @@ function clearChats() { | |||
| return initialState; | |||
| } | |||
| function addNewMessage(state, { payload }) { | |||
| console.log(state); | |||
| console.log(payload); | |||
| let allChats = [...state.latestChats]; | |||
| let chat = allChats.find((item) => item.chat._id === payload._id); | |||
| if (chat) { | |||
| @@ -60,9 +58,6 @@ function addNewMessage(state, { payload }) { | |||
| } else { | |||
| newSelectedChat = { ...chat }; | |||
| } | |||
| console.log("chat", chat) | |||
| console.log("allChats", allChats); | |||
| console.log("newSelectedChat", newSelectedChat); | |||
| return { | |||
| ...state, | |||
| latestChats: [...allChats], | |||
| @@ -109,10 +109,8 @@ function* startNewChat(payload) { | |||
| const userId = yield select(selectUserId); | |||
| const data = yield call(attemptFetchChats, userId); | |||
| yield put(setChats([...data.data])); | |||
| console.log(newChatData); | |||
| const newChatId = newChatData.data.chatId; | |||
| yield put(startNewChatSuccess()); | |||
| console.log(payload); | |||
| yield call( | |||
| SendMessageSocket, | |||
| newChatData.data.chatId, | |||
| @@ -0,0 +1,12 @@ | |||
| export default function getBase64(file) { | |||
| var reader = new FileReader(); | |||
| reader.readAsDataURL(file); | |||
| let base64file = ""; | |||
| reader.onload = function () { | |||
| base64file = reader.result; | |||
| }; | |||
| reader.onerror = function (error) { | |||
| console.log('Error: ', error); | |||
| }; | |||
| return base64file; | |||
| } | |||
| @@ -4,41 +4,44 @@ const CLOUDFLARE = "CLOUDFLARE"; | |||
| const IMAGE_PLATFORM = CLOUDFLARE; | |||
| export const variants = { | |||
| offerCard: "offerCard", | |||
| offerDetails: "offerDetails", | |||
| profileImage: "profileImage", | |||
| reviewCard: "reviewCard", | |||
| chatHeader: "chatHeader", | |||
| chatMessage: "chatMessage", | |||
| chatCard: "chatCard", | |||
| deleteChat: "chatHeader", | |||
| profileCard: "profileCard", | |||
| createReviewCard: "createReviewCard" | |||
| } | |||
| offerCard: "offerCard", | |||
| offerDetails: "offerDetails", | |||
| profileImage: "profileImage", | |||
| reviewCard: "reviewCard", | |||
| chatHeader: "chatHeader", | |||
| chatMessage: "chatMessage", | |||
| chatCard: "chatCard", | |||
| deleteChat: "chatHeader", | |||
| profileCard: "profileCard", | |||
| createReviewCard: "createReviewCard", | |||
| }; | |||
| const cloudFlareVariants = { | |||
| offerCard: "primary", | |||
| offerCardMobile: "primaryMobile", | |||
| offerDetails: "primary", | |||
| offerDetailsMobile: "primary", | |||
| profileImage: "primary", | |||
| profileImageMobile: "profileMobile", | |||
| reviewCard: "review", | |||
| reviewCardMobile: "review", | |||
| chatHeader: "chatHeader", | |||
| chatHeaderMobile: "chatHeader", | |||
| chatMessage: "chatHeader", | |||
| chatMessageMobile: "chatHeader", | |||
| chatCard: "chatCard", | |||
| chatCardMobile: "chatCard", | |||
| profileCard: "profileCard", | |||
| createReviewCard: "primaryMobile", | |||
| } | |||
| offerCard: "primary", | |||
| offerCardMobile: "primaryMobile", | |||
| offerDetails: "primary", | |||
| offerDetailsMobile: "primary", | |||
| profileImage: "primary", | |||
| profileImageMobile: "profileMobile", | |||
| reviewCard: "review", | |||
| reviewCardMobile: "review", | |||
| chatHeader: "chatHeader", | |||
| chatHeaderMobile: "chatHeader", | |||
| chatMessage: "chatHeader", | |||
| chatMessageMobile: "chatHeader", | |||
| chatCard: "chatCard", | |||
| chatCardMobile: "chatCard", | |||
| profileCard: "profileCard", | |||
| createReviewCard: "primaryMobile", | |||
| }; | |||
| export const getImageUrl = (imageUrl, variant, isMobile = false) => { | |||
| let imageVariant = ""; | |||
| if (IMAGE_PLATFORM === CLOUDFLARE) { | |||
| imageVariant = isMobile ? cloudFlareVariants[variant + "Mobile"] : cloudFlareVariants[variant]; | |||
| } | |||
| return imageUrl + imageVariant; | |||
| } | |||
| let imageVariant = ""; | |||
| console.log(imageUrl); | |||
| console.log(variant); | |||
| if (IMAGE_PLATFORM === CLOUDFLARE) { | |||
| imageVariant = isMobile | |||
| ? cloudFlareVariants[variant + "Mobile"] | |||
| : cloudFlareVariants[variant]; | |||
| } | |||
| return imageUrl + imageVariant; | |||
| }; | |||