Bladeren bron

All functionalities for feature 607 work

feature/607
Djordje Mitrovic 3 jaren geleden
bovenliggende
commit
501f2a89af
60 gewijzigde bestanden met toevoegingen van 888 en 472 verwijderingen
  1. 52
    41
      README.md
  2. 1
    1
      package.json
  3. 6
    119
      src/App.js
  4. 3
    1
      src/components/Cards/ChatCard/ChatCard.js
  5. 0
    1
      src/components/Cards/ChatCard/ChatCommands/ChatCommands.js
  6. 11
    1
      src/components/Cards/ChatCard/LittleOfferDetails/LittleOfferDetails.js
  7. 3
    3
      src/components/Cards/CreateOfferCard/CreateOffer.js
  8. 3
    5
      src/components/Cards/FilterCard/FilterFooter/FilterFooter.js
  9. 15
    6
      src/components/Cards/ItemDetailsCard/OfferDetails/OfferDetails.js
  10. 39
    23
      src/components/Cards/LittleOfferCard/LittleOfferCard.js
  11. 11
    5
      src/components/Cards/MessageCard/MessageCard.js
  12. 5
    5
      src/components/Cards/MessageCard/MessageCard.styled.js
  13. 11
    1
      src/components/Cards/MiniChatCard/MiniChatCard.js
  14. 11
    1
      src/components/Cards/OfferCard/DeleteOffer/DeleteOffer.js
  15. 12
    1
      src/components/Cards/OfferCard/OfferCard.js
  16. 25
    5
      src/components/Cards/UserReviewsCard/UserReviewsCard.js
  17. 12
    1
      src/components/ChatColumn/ChatColumn.js
  18. 4
    1
      src/components/CreateReview/FirstStep/FirstStepCreateReview.js
  19. 1
    0
      src/components/CreateReview/SecondStep/SecondStepCreateReview.js
  20. 46
    4
      src/components/DirectChat/DirectChat.js
  21. 8
    6
      src/components/DirectChat/DirectChatContent/DirectChatContent.js
  22. 14
    6
      src/components/DirectChat/DirectChatContent/DirectChatContentHeader/DirectChatContentHeader.js
  23. 94
    21
      src/components/DirectChat/DirectChatNewMessage/DirectChatNewMessage.js
  24. 0
    1
      src/components/Header/AboutHeader/AboutHeader.js
  25. 1
    1
      src/components/Header/Header.js
  26. 31
    14
      src/components/ImagePicker/ImagePicker.js
  27. 11
    1
      src/components/ItemDetails/ItemDetailsHeaderCard/ItemDetailsHeaderCard.js
  28. 1
    1
      src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/Category/CategoryDetail.js
  29. 1
    1
      src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/Category/CategoryDetail.styled.js
  30. 1
    4
      src/components/Login/Login.js
  31. 1
    0
      src/components/MarketPlace/Offers/OffersNotFound.styled.js
  32. 47
    32
      src/components/Popovers/HeaderPopover/HeaderPopover.js
  33. 9
    4
      src/components/Popovers/MyMessages/MyMessages.js
  34. 35
    32
      src/components/Popovers/MyPosts/MyPosts.js
  35. 0
    1
      src/components/Profile/ProfileOffers/ProfileOffers.js
  36. 16
    4
      src/components/ProfileCard/EditProfile/EditProfile.js
  37. 26
    1
      src/components/ProfileCard/EditProfile/EditProfile.styled.js
  38. 11
    1
      src/components/ProfileCard/ProfileMainInfo/ProfileMainInfo.js
  39. 6
    5
      src/components/TextFields/AutoSuggestTextField/AutoSuggestTextField.js
  40. 11
    3
      src/components/TextFields/AutoSuggestTextField/AutoSuggestTextField.styled.js
  41. 0
    1
      src/components/UserReviews/UserReviews.js
  42. 0
    3
      src/hooks/useOffers/useSearch.js
  43. 1
    0
      src/i18n/resources/rs.js
  44. 3
    5
      src/pages/About/AboutPage.js
  45. 0
    1
      src/pages/ItemDetailsPage/ItemDetailsPageMUI.js
  46. 0
    1
      src/pages/RegisterPages/Register/FirstPart/FirstPartOfRegistration.js
  47. 0
    1
      src/pages/RegisterPages/Register/Register.js
  48. 32
    0
      src/socket/socket.js
  49. 1
    0
      src/store/actions/chat/chatActionConstants.js
  50. 5
    1
      src/store/actions/chat/chatActions.js
  51. 4
    1
      src/store/middleware/accessTokensMiddleware.js
  52. 61
    22
      src/store/reducers/chat/chatReducer.js
  53. 38
    5
      src/store/saga/chatSaga.js
  54. 1
    1
      src/store/saga/loginSaga.js
  55. 43
    5
      src/store/saga/offersSaga.js
  56. 45
    30
      src/store/saga/profileSaga.js
  57. 13
    24
      src/store/saga/registerSaga.js
  58. 1
    3
      src/util/helpers/chatHelper.js
  59. 44
    0
      src/util/helpers/imageUrlGetter.js
  60. 1
    4
      src/validations/editProfileValidation.js

+ 52
- 41
README.md Bestand weergeven

@@ -1,70 +1,81 @@
# Getting Started with Create React App
# Trampa API Frontend

This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).

## Available Scripts
Welcome to frontend application for trampa website. In next sections we will walk you through our project, setting it up on your computer, building it for production and last but not the least walk you through tools that we used:

In the project directory, you can run:
- [Description](#Description)
- [Setup](#Setup)
- [Testing](#Testing)

### `npm start`
# Description
This is an internal project for e-commerce website called Trampa that was done by Diligent.
Our project is hosted on our local [git](https://git.dilig.net/selenaaasi/trampa-frontend)

Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
## Tech stack

The page will reload if you make edits.\
You will also see any lint errors in the console.
In this project we used Node and N(ode)P(ackage)M(anager) for initializing project
and importing a lot of libraries that helps us developing.

### `npm test`
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
Every HTTP request is implemented via Axios, and for handling asynchronous code we used Saga.

Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
## Node Package Manager

### `npm run build`
In order to run this project you need to install Node that comes with its Package Manager.
Please follow the official tutorial on installing Node which can be found on this link:
> https://docs.npmjs.com/downloading-and-installing-node-js-and-npm

Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
## Development environment
- [Visual Studio Code](https://code.visualstudio.com/Download) - Used as the primary code editor
- [npm](https://www.npmjs.com/) - Used for installing node and running our application
- [git](https://git-scm.com/downloads)

The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
# Setup

See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
Here we will show you how to set up project and run it in localhost.
## Project setup
In order to run our project you need to clone it from [git](https://git.dilig.net/selenaaasi/trampa-frontend.git) first. Open terminal (cmd and powershell work as well) in folder that you want this project to be and run command
> git clone http://git.dilig.net/selenaaasi/trampa-frontend.git

### `npm run eject`
After cloning project you can open it with your preferred IDE/Code editor if you want to see the code. Before running project you need to open terminal and run command
> npm install

**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
Running that command will download all necessary npm packages to run the project.

If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Now your project is ready for setup on both local and production environment.

Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
### Local project setup

You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
You can run the project using
> npm start

## Learn More
Congratulations! You now run website application on following website:
> http://localhost:3000/

You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
### Production project setup

To learn React, check out the [React documentation](https://reactjs.org/).
You can make the production build using
> npm run build

### Code Splitting

This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)

### Analyzing the Bundle Size
Congratulations! You now have production build which is ready for deploy!
The build is minified and the filenames include the hashes.
It correctly bundles React in production mode and optimizes the build for the best performance.

This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
When your application is deployed you can run it using:
> npx serve -s build

### Making a Progressive Web App
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.

This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
# Testing
Here we will show you how to run tests and see code coverage

### Advanced Configuration
## Testing framework
We needed to write tests, unit, integration and E2E. Framework that we used for that is called [jest](https://www.npmjs.com/package/jest).

This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
When we write our tests we can use following comand:
> npm test

### Deployment
which launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.

This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)

### `npm run build` fails to minify

This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

+ 1
- 1
package.json Bestand weergeven

@@ -1,6 +1,6 @@
{
"name": "web",
"version": "3.0.4",
"version": "3.0.6",
"private": true,
"dependencies": {
"@emotion/react": "^11.5.0",

+ 6
- 119
src/App.js Bestand weergeven

@@ -1,6 +1,5 @@
/*eslint-disable*/
import React, { useState, useEffect } from "react";
import io from "socket.io-client";
import { Router } from "react-router-dom";
import { Helmet } from "react-helmet-async";
import i18next from "i18next";
@@ -11,120 +10,17 @@ import { StyledEngineProvider } from "@mui/material";
import GlobalStyle from "./components/Styles/globalStyles";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import { socketInit } from "./socket/socket";
import { useSelector } from "react-redux";
import { selectUserId } from "./store/selectors/loginSelectors";

const URL = "https://trampa-api-test.dilig.net";
const socket2 = io("https://trampa-api-test.dilig.net", { autoConnect: true, reconnectionAttempts: 5 });
const socket = io("https://trampa-api-test.dilig.net", {
autoConnect: true,
transports: ["websocket"],
reconnectionAttempts: 5,
});
const socket4 = io("https://trampa-api-test.dilig.net", { autoConnect: true, reconnectionAttempts: 5 });
const socket3 = io("https://trampa-api-test.dilig.net", {
autoConnect: true,
transports: ["websocket"],
reconnectionAttempts: 5,
});
const App = () => {
console.log(socket);
console.log(socket2);
const userId = useSelector(selectUserId);

const [isConnected, setIsConnected] = useState(socket.connected);
const [lastPong, setLastPong] = useState(null);
console.log();
useEffect(() => {
socket.auth = {
// userId: "62de57c6dff6f986e43d14ec",
userId: "62ff762554ec55060e3a456b",
sessionID: localStorage.getItem("sessionID"),
};
socket.on("connect", (client) => {
console.log("client: ", client);
setIsConnected(true);
});
socket2.on("connect", (client) => {
console.log("client: ", client);
setIsConnected(true);
});
socket3.on("connect", (client) => {
console.log("client: ", client);
setIsConnected(true);
});
socket4.on("connect", (client) => {
console.log("client: ", client);
setIsConnected(true);
});
socket.on("session", ({ sessionID, userID }) => {
localStorage.setItem("sessionID", sessionID);
localStorage.setItem("userID", userID);
console.log("sessionID: ", sessionID);
console.log("userID: ", userID);
});
// socket.on("connect_error", (err) => {
// console.log(err);
// });
socketInit(userId);
}, [userId]);

socket.on("connection", (client) => {
console.log(client);
});

socket.on("disconnect", () => {
setIsConnected(false);
});

// socket.on("user disconnected", (userID) => {
// console.log(userID);
// });

// // socket.on('emit', (client) => {
// // console.log(client);
// // })
// socket.on("sokkk", (clg) => {
// console.log(clg);
// });
// // socket.onAny((event, ...args) => {
// // console.log(event, args);
// // });
// socket.on("povratna", (data) => {
// console.log(data);
// });
// socket.on("private_message", (data) => {
// console.log(data);
// });

// // socket.open;

// socket.on("pong", () => {
// setLastPong(new Date().toISOString());
// });

// // socket.connect();

return () => {
socket.off("connect");
socket.off("disconnect");
socket.off("pong");
socket.off("reconnection_attempt")
};
}, []);
const handleClick = () => {
// socket.connect();
// socket.emit("sokkk 2", "sock");
// socket.emit("sock")
};
const sendPing = () => {
socket.emit("private_message", {
text: "Probica",
// toUserId: "62de5844dff6f986e43d14f6",
toUserId: "62de57c6dff6f986e43d14ec",
chatId: "62eb8424632e1112ef467750",
});
};
const disconnect = () => {
// socket.disconnect();
socket.disconnect();
};
return (
<Router history={history}>
<Helmet>
@@ -134,15 +30,6 @@ const App = () => {
<Header />
<GlobalStyle />
<ToastContainer />
{/* <div style={{ position: "relative", top: "100px", left: "400px" }}>
<p>Connected: {"" + isConnected}</p>
<br />
<p>Last pong: {lastPong || "-"}</p>
<br />
<button onClick={sendPing}>Send ping</button>
<br />
<button onClick={disconnect}>Disconnect</button>
</div> */}
<AppRoutes />
</StyledEngineProvider>
</Router>

+ 3
- 1
src/components/Cards/ChatCard/ChatCard.js Bestand weergeven

@@ -16,6 +16,7 @@ import MobileOfferDetails from "./MobileOfferDetails/MobileOfferDetails";
import OfferLocation from "./OfferLocation/OfferLocation";
import ChatCommands from "./ChatCommands/ChatCommands";
import useIsMobile from "../../../hooks/useIsMobile";
import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter";

const ChatCard = (props) => {
const history = useHistory();
@@ -40,7 +41,8 @@ const ChatCard = (props) => {
>
<Col>
<UserImgWrapper>
<UserImage src={chat?.interlocutorData?.image} />
{/* <UserImage src={chat?.interlocutorData?.image} /> */}
<UserImage src={getImageUrl(chat?.interlocutorData?.image, variants.chatCard, isMobile)} />
</UserImgWrapper>

<ChatInfo>

+ 0
- 1
src/components/Cards/ChatCard/ChatCommands/ChatCommands.js Bestand weergeven

@@ -20,7 +20,6 @@ const ChatCommands = (props) => {
<Commands>
<PhoneIconContainer
onClick={(event) => {
console.log(event);
setShowPhonePopover(true);
setPhonePopoverAnchorEl(event.currentTarget);
}}

+ 11
- 1
src/components/Cards/ChatCard/LittleOfferDetails/LittleOfferDetails.js Bestand weergeven

@@ -10,15 +10,25 @@ import {
} from "./LittleOfferDetails.styled";
import { useTranslation } from "react-i18next";
import { Col } from "../ChatCard.styled";
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();
return (
<Col mobileDisappear>
<ChatOffer>
<OfferImgWrapper>
<OfferImage src={chat?.offerData?.firstImage} />
{/* <OfferImage src={chat?.offerData?.firstImage} /> */}
<OfferImage
src={getImageUrl(
chat?.offerData?.firstImage,
variants.offerCard,
isMobile
)}
/>
</OfferImgWrapper>
<OfferCardContainer>
<OfferText>{t("messages.cardProduct")}</OfferText>

+ 3
- 3
src/components/Cards/CreateOfferCard/CreateOffer.js Bestand weergeven

@@ -53,9 +53,9 @@ const CreateOffer = ({ closeCreateOfferModal, editOffer, offer }) => {
.filter((img) => img !== undefined)
.map((img) =>
img
.replace("data:image/jpg;base64,", "")
.replace("data:image/jpeg;base64,", "")
.replace("data:image/png;base64,", "")
// .replace("data:image/jpg;base64,", "")
// .replace("data:image/jpeg;base64,", "")
// .replace("data:image/png;base64,", "")
);

const offerData = {

+ 3
- 5
src/components/Cards/FilterCard/FilterFooter/FilterFooter.js Bestand weergeven

@@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import React from "react";
import PropTypes from "prop-types";
import { FilterFooterContainer } from "./FilterFooter.styled";
import selectedTheme from "../../../../themes";
@@ -8,15 +8,13 @@ import useIsMobile from "../../../../hooks/useIsMobile";

const FilterFooter = (props) => {
const { t } = useTranslation();
const {isMobile }= useIsMobile();
const { isMobile } = useIsMobile();
const filters = props.filters;
const handleFilters = () => {
filters.paging.changePage(1);
filters.apply();
props.toggleFilters();
};
useEffect(() => {
console.log(isMobile);
}, [isMobile])
return (
<FilterFooterContainer responsiveOpen={isMobile}>
{isMobile && (

+ 15
- 6
src/components/Cards/ItemDetailsCard/OfferDetails/OfferDetails.js Bestand weergeven

@@ -15,11 +15,14 @@ import {
import { useTranslation } from "react-i18next";
import useScreenDimensions from "../../../../hooks/useScreenDimensions";
import { formatDateLocale } from "../../../../util/helpers/dateHelpers";
import useIsMobile from "../../../../hooks/useIsMobile";
import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter";

const OfferDetails = (props) => {
const offer = props.offer;
const { t } = useTranslation();
const dimension = useScreenDimensions();
const { isMobile } = useIsMobile();
const date = formatDateLocale(new Date(offer?.offer?._created));
return (
<Details
@@ -29,15 +32,21 @@ const OfferDetails = (props) => {
>
{dimension.width < 600 || !props.singleOffer ? (
<ScrollerHorizontal>
{offer?.offer?.images?.map((item) => {
return <OfferImage src={item} key={item} />;
})}
{offer?.offer?.images?.map((item) => (
<OfferImage
src={getImageUrl(item, variants.offerCard, isMobile)}
key={item}
/>
))}
</ScrollerHorizontal>
) : (
<ScrollerVertical>
{offer?.offer?.images?.map((item) => {
return <OfferImage src={item} key={item} />;
})}
{offer?.offer?.images?.map((item) => (
<OfferImage
src={getImageUrl(item, variants.offerCard, isMobile)}
key={item}
/>
))}
</ScrollerVertical>
)}
<OfferInfoContainer singleOffer={props.singleOffer}>

+ 39
- 23
src/components/Cards/LittleOfferCard/LittleOfferCard.js Bestand weergeven

@@ -1,28 +1,44 @@
import React from 'react'
import PropTypes from 'prop-types'
import { LittleOfferCardContainer, OfferCategory, OfferCategoryIcon, OfferDetails, OfferImage, OfferName, OfferSwapsIcon, OfferSwapsIconContainer } from './LittleOfferCard.styled'
import React from "react";
import PropTypes from "prop-types";
import {
LittleOfferCardContainer,
OfferCategory,
OfferCategoryIcon,
OfferDetails,
OfferImage,
OfferName,
OfferSwapsIcon,
OfferSwapsIconContainer,
} from "./LittleOfferCard.styled";
import useIsMobile from "../../../hooks/useIsMobile";
import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter";

const LittleOfferCard = (props) => {
return (
<LittleOfferCardContainer>
<OfferImage src={props.image} />
<OfferDetails>
<OfferName>{props.name}</OfferName>
<OfferCategory>
<OfferCategoryIcon />
{props.categoryName}</OfferCategory>
</OfferDetails>
<OfferSwapsIconContainer>
<OfferSwapsIcon />
</OfferSwapsIconContainer>
</LittleOfferCardContainer>
)
}
const { isMobile } = useIsMobile();
return (
<LittleOfferCardContainer>
{/* <OfferImage src={props.image} /> */}
<OfferImage
src={getImageUrl(props.image, variants.reviewCard, isMobile)}
/>
<OfferDetails>
<OfferName>{props.name}</OfferName>
<OfferCategory>
<OfferCategoryIcon />
{props.categoryName}
</OfferCategory>
</OfferDetails>
<OfferSwapsIconContainer>
<OfferSwapsIcon />
</OfferSwapsIconContainer>
</LittleOfferCardContainer>
);
};

LittleOfferCard.propTypes = {
image: PropTypes.string,
name: PropTypes.string,
categoryName: PropTypes.string,
}
image: PropTypes.string,
name: PropTypes.string,
categoryName: PropTypes.string,
};

export default LittleOfferCard
export default LittleOfferCard;

+ 11
- 5
src/components/Cards/MessageCard/MessageCard.js Bestand weergeven

@@ -8,19 +8,25 @@ import {
ProfileImage,
} from "./MessageCard.styled";
import { formatDateTime } from "../../../util/helpers/dateHelpers";
import useIsMobile from "../../../hooks/useIsMobile";
import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter";

const MessageCard = (props) => {
const message = props.message;
const { isMobile } = useIsMobile();

const dateString = formatDateTime(new Date(message._created));
return (
<MessageCardContainer isMyMessage={props.isMyMessage}>
<ProfileImage src={props.image} />
<MessageContent isMyMessage={props.isMyMessage}>
<MessageText isMyMessage={props.isMyMessage}>
<MessageCardContainer ismymessage={props.isMyMessage}>
{/* <ProfileImage src={props.image} /> */}
<ProfileImage
src={getImageUrl(props.image, variants.chatMessage, isMobile)}
/>
<MessageContent ismymessage={props.isMyMessage}>
<MessageText ismymessage={props.isMyMessage}>
{props.message.text}
</MessageText>
<MessageDate isMyMessage={props.isMyMessage}>{dateString}</MessageDate>
<MessageDate ismymessage={props.isMyMessage}>{dateString}</MessageDate>
</MessageContent>
</MessageCardContainer>
);

+ 5
- 5
src/components/Cards/MessageCard/MessageCard.styled.js Bestand weergeven

@@ -4,7 +4,7 @@ import selectedTheme from "../../../themes";

export const MessageCardContainer = styled(Box)`
display: flex;
flex-direction: ${props => props.isMyMessage ? `row-reverse` : `row`};
flex-direction: ${props => props.ismymessage ? `row-reverse` : `row`};
margin-bottom: 18px;
`;
export const ProfileImage = styled.img`
@@ -18,11 +18,11 @@ export const ProfileImage = styled.img`
`;
export const MessageContent = styled(Box)`
background-color: ${(props) =>
props.isMyMessage
props.ismymessage
? selectedTheme.primaryPurple
: selectedTheme.messageBackground};
border-radius: ${(props) =>
props.isMyMessage ? "9px 0px 9px 9px" : "0px 9px 9px 9px"};
props.ismymessage ? "9px 0px 9px 9px" : "0px 9px 9px 9px"};
padding: 9px;
position: relative;
min-height: 65px;
@@ -36,10 +36,10 @@ export const MessageText = styled(Typography)`
font-family: "DM Sans";
font-size: 16px;
line-height: 22px;
color: ${props => props.isMyMessage ? `white` : selectedTheme.messageText};
color: ${props => props.ismymessage ? `white` : selectedTheme.messageText};
`;
export const MessageDate = styled(Typography)`
color: ${props => props.isMyMessage ? selectedTheme.messageMyDate : selectedTheme.messageDate};
color: ${props => props.ismymessage ? selectedTheme.messageMyDate : selectedTheme.messageDate};
font-size: 12px;
font-style: italic;
position: absolute;

+ 11
- 1
src/components/Cards/MiniChatCard/MiniChatCard.js Bestand weergeven

@@ -10,16 +10,26 @@ import {
} from "./MiniChatCard.styled";
import { useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
import useIsMobile from "../../../hooks/useIsMobile";
import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter";

const MiniChatCard = (props) => {
const { t } = useTranslation();
const history = useHistory();
const { isMobile } = useIsMobile();
const changeChat = () => {
history.push(`/messages/${props?.chat?.chat?._id}`);
};
return (
<MiniChatCardContainer selected={props.selected} onClick={changeChat}>
<ProfileImage src={props?.chat?.interlocutorData?.image} />
{/* <ProfileImage src={props?.chat?.interlocutorData?.image} /> */}
<ProfileImage
src={getImageUrl(
props?.chat?.interlocutorData?.image,
variants.chatCard,
isMobile
)}
/>
<ProfileDetails>
<ProfileName selected={props.selected}>
{props?.chat?.interlocutorData?.name}

+ 11
- 1
src/components/Cards/OfferCard/DeleteOffer/DeleteOffer.js Bestand weergeven

@@ -26,12 +26,15 @@ import {
} from "../../../../store/actions/offers/offersActions";
import { useTranslation, Trans } from "react-i18next";
import { useHistory } from "react-router-dom";
import useIsMobile from "../../../../hooks/useIsMobile";
import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter";

const DeleteOffer = (props) => {
const dispatch = useDispatch();
const { t } = useTranslation();
const history = useHistory();
const userId = props.offer.userId;
const { isMobile } = useIsMobile();
const offerId = props.offer._id;
const closeDeleteModalHandler = () => {
props.closeModalHandler();
@@ -59,7 +62,14 @@ const DeleteOffer = (props) => {
<DeleteOfferContainer>
<OfferInfo>
<OfferImageContainer>
<OfferImage src={props.offer.images[0]} />
{/* <OfferImage src={props.offer.images[0]} /> */}
<OfferImage
src={getImageUrl(
props.offer.images[0],
variants.deleteChat,
isMobile
)}
/>
</OfferImageContainer>
<OfferDescription>
<OfferDescriptionTitle>{props.offer.name}</OfferDescriptionTitle>

+ 12
- 1
src/components/Cards/OfferCard/OfferCard.js Bestand weergeven

@@ -39,12 +39,15 @@ import { useHistory } from "react-router-dom";
import CreateOffer from "../CreateOfferCard/CreateOffer";
import { useSelector } from "react-redux";
import { selectUserId } from "../../../store/selectors/loginSelectors";
import useIsMobile from "../../../hooks/useIsMobile";
import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter";

const OfferCard = (props) => {
const [deleteOfferModal, setDeleteOfferModal] = useState(false);
const [editOfferModal, setEditOfferModal] = useState(false);
const history = useHistory();
const userId = useSelector(selectUserId);
const { isMobile } = useIsMobile();

const routeToItem = (itemId) => {
history.push(`/proizvodi/${itemId}`);
@@ -99,7 +102,15 @@ const OfferCard = (props) => {
<OfferFlexContainer vertical={props.vertical}>
<OfferImageContainer vertical={props.vertical}>
<OfferImage
src={props?.offer?.images ? props?.offer?.images[0] : ""}
src={
props?.offer?.images
? getImageUrl(
props?.offer?.images[0],
variants.offerCard,
isMobile
)
: ""
}
vertical={props.vertical}
></OfferImage>
</OfferImageContainer>

+ 25
- 5
src/components/Cards/UserReviewsCard/UserReviewsCard.js Bestand weergeven

@@ -18,10 +18,15 @@ import {
import { ListItem } from "@mui/material";
import selectedTheme from "../../../themes";
import { useTranslation } from "react-i18next";
import useIsMobile from "../../../hooks/useIsMobile";
import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter";
import ReviewOffer from "./ReviewOffer/ReviewOffer";
import { reviewEnum } from "../../../enums/reviewEnum";

const UserReviewsCard = (props) => {
const { t } = useTranslation();
const { isMobile } = useIsMobile();
console.log(props);

const review = useMemo(() => {
if (props.givingReview) {
@@ -29,12 +34,24 @@ const UserReviewsCard = (props) => {
...props.review,
};
}
console.log(props.review);
let isSuccessfulSwap = "DA";
if (props.review.succeeded === "failed") isSuccessfulSwap = "NE";
if (
props.review.succeeded === "failed" ||
props.review.isSuccessfulSwap === reviewEnum.NO.mainText
)
isSuccessfulSwap = "NE";
let isGoodCommunication = "DA";
if (props.review.communication === "could be better")
if (
props.review.communication === "could be better" ||
props.review.isCorrectCommunication === reviewEnum.NOT_BAD.mainText
)
isGoodCommunication = "MOŽE BOLJE";
if (props.review.communication === "no") isGoodCommunication = "NE";
if (
props.review.communication === "no" ||
props.review.isCorrectCommunication === reviewEnum.NO.mainText
)
isGoodCommunication = "NE";
return {
name: props.review.companyName,
image: props.review.image,
@@ -48,7 +65,10 @@ const UserReviewsCard = (props) => {
<ReviewContainer key={review?.image}>
<ListItem alignItems="flex-start" sx={{ alignItems: "center", mt: 2 }}>
<ProfileImageContainer>
<ProfileImage alt={review?.name} src={review?.image} />
{/* <ProfileImage alt={review?.name} src={review?.image} /> */}
<ProfileImage
src={getImageUrl(review?.image, variants.reviewCard, isMobile)}
/>
</ProfileImageContainer>
<ProfileName sx={{ color: selectedTheme.primaryPurple }}>
<b>{review?.name}</b>
@@ -63,7 +83,7 @@ const UserReviewsCard = (props) => {
sx={{ pl: 2, py: 2 }}
>
<ThumbBox item>
{review.isSuccessfulSwap === "DA" ? (
{review.isSuccessfulSwap === reviewEnum.YES.mainText ? (
<ThumbUp color="success" />
) : (
<ThumbDown color="error" />

+ 12
- 1
src/components/ChatColumn/ChatColumn.js Bestand weergeven

@@ -18,8 +18,9 @@ import { HeaderTitle } from "../ProfileCard/ProfileCard.styled";
import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
import { selectLatestChats } from "../../store/selectors/chatSelectors";
import { fetchChats } from "../../store/actions/chat/chatActions";
import { addNewMessage, fetchChats } from "../../store/actions/chat/chatActions";
import useSorting from "../../hooks/useOffers/useSorting";
import { addMesageListener, removeMessageListener } from "../../socket/socket";

export const DownArrow = (props) => {
<IconStyled {...props}>
@@ -38,6 +39,16 @@ export const ChatColumn = () => {
dispatch(fetchChats());
}, []);

useEffect(() => {
addMesageListener((data) => {
dispatch(addNewMessage({
_id: data.chatId,
message: data.message
}))
});
return () => removeMessageListener();
}, [])

useEffect(() => {
setSortOption(sorting.selectedSortOption);
}, [sorting.selectedSortOption]);

+ 4
- 1
src/components/CreateReview/FirstStep/FirstStepCreateReview.js Bestand weergeven

@@ -19,6 +19,7 @@ import selectedTheme from "../../../themes";
import { useFormik } from "formik";
import * as Yup from "yup";
import useIsMobile from "../../../hooks/useIsMobile";
import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter";

const FirstStepCreateReview = (props) => {
const offer = props.offer;
@@ -26,6 +27,7 @@ const FirstStepCreateReview = (props) => {
const { isMobile } = useIsMobile();
const { t } = useTranslation();
const handleSubmit = (values) => {
console.log(values)
props.goToNextStep(values);
};

@@ -64,7 +66,8 @@ const FirstStepCreateReview = (props) => {
>
<CreateReviewTitle>{t("reviews.modalTitle")}</CreateReviewTitle>
<ProfileImageContainer>
<ProfileImage src={interlocutor.image} />
{/* <ProfileImage src={interlocutor.image} /> */}
<ProfileImage src={getImageUrl(interlocutor.image, variants.createReviewCard, false)} />
</ProfileImageContainer>
<ProfileName>{interlocutor.name}</ProfileName>
<LittleOfferCard

+ 1
- 0
src/components/CreateReview/SecondStep/SecondStepCreateReview.js Bestand weergeven

@@ -14,6 +14,7 @@ const SecondStepCreateReview = (props) => {
const goToNextStep = () => {
props.goToNextStep();
}
console.log(props.review);

return (
<SecondStepCreateReviewContainer>

+ 46
- 4
src/components/DirectChat/DirectChat.js Bestand weergeven

@@ -5,21 +5,34 @@ import DirectChatHeaderTitle from "./DirectChatHeaderTitle/DirectChatHeaderTitle
import DirectChatHeader from "./DirectChatHeader/DirectChatHeader";
import { useDispatch, useSelector } from "react-redux";
import { useLocation, useRouteMatch } from "react-router-dom";
import { fetchOneChat, setOneChat } from "../../store/actions/chat/chatActions";
import { selectSelectedChat } from "../../store/selectors/chatSelectors";
import {
addNewMessage,
fetchChats,
fetchOneChat,
setOneChat,
} from "../../store/actions/chat/chatActions";
import {
selectLatestChats,
selectSelectedChat,
} from "../../store/selectors/chatSelectors";
import DirectChatContent from "./DirectChatContent/DirectChatContent";
import { selectOffer } from "../../store/selectors/offersSelectors";
import { fetchOneOffer } from "../../store/actions/offers/offersActions";
import SkeletonDirectChat from "./SkeletonDirectChat/SkeletonDirectChat";
import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors";
import { CHAT_SCOPE } from "../../store/actions/chat/chatActionConstants";
import { selectUserId } from "../../store/selectors/loginSelectors";
import { addMesageListener, removeMessageListener } from "../../socket/socket";

const DirectChat = () => {
const chat = useSelector(selectSelectedChat);
const allChats = useSelector(selectLatestChats);
const offer = useSelector(selectOffer);
const routeMatch = useRouteMatch();
const location = useLocation();
const dispatch = useDispatch();

const userId = useSelector(selectUserId);
const isLoadingDirectChat = useSelector(
selectIsLoadingByActionType(CHAT_SCOPE)
);
@@ -35,7 +48,7 @@ const DirectChat = () => {
if (location?.state?.offerId) {
return {};
}
return chat?.chat;
return chat;
}, [chat, location.state]);

const interlocutorObject = useMemo(() => {
@@ -44,9 +57,16 @@ const DirectChat = () => {
image: offer?.companyData?.image,
name: offer?.companyData?.company?.name,
location: offer?.companyData?.company?.contacts?.location,
userId: offer?.offer?.userId,
};
}
return chat?.interlocutor;
return {
...chat?.interlocutor,
userId:
chat?.chat?.participants[0] === userId
? chat?.chat?.participants[1]
: chat?.chat?.participants[0],
};
}, [chat, location.state, offer]);

useEffect(() => {
@@ -55,6 +75,28 @@ const DirectChat = () => {
}
}, [routeMatch.params.idChat, location.state?.offerId]);

useEffect(() => {
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;
})
) {
dispatch(
addNewMessage({
_id: data.chatId,
message: data.message,
})
);
} else {
dispatch(fetchChats());
}
});
return () => removeMessageListener();
}, [allChats]);

const refreshChat = () => {
if (routeMatch.params.idChat === "newMessage") {
dispatch(fetchOneOffer(location.state.offerId));

+ 8
- 6
src/components/DirectChat/DirectChatContent/DirectChatContent.js Bestand weergeven

@@ -16,7 +16,7 @@ import { selectIsLoadingByActionType } from "../../../store/selectors/loadingSel
import { CHAT_SCOPE } from "../../../store/actions/chat/chatActionConstants";

const DirectChatContent = (props) => {
const messages = props?.chat?.messages;
const messages = props?.chat?.chat?.messages;
const userId = useSelector(selectUserId);
const myProfileImage = useSelector(selectMineProfilePicture);
const messagesRef = useRef(null);
@@ -28,9 +28,9 @@ const DirectChatContent = (props) => {
props.refreshChat();
};
useEffect(() => {
const offsetBottom =
messagesRef.current?.offsetTop + messagesRef.current?.offsetHeight;
messagesRef.current?.scrollTo({ top: offsetBottom, behaviour: "smooth" });
// const offsetBottom =
// messagesRef.current?.offsetTop + messagesRef.current?.offsetHeight;
messagesRef.current?.scrollTo({ top: messagesRef.current.scrollHeight, behaviour: "smooth" });
}, [messages]);
return (
<>
@@ -46,7 +46,7 @@ const DirectChatContent = (props) => {
? myProfileImage
: interlucatorProfileImage;
return (
<MessageContainer key={item?._id}>
<MessageContainer key={item?._id || item?._created}>
<MessageCard
message={item}
image={image}
@@ -57,8 +57,10 @@ const DirectChatContent = (props) => {
})}
</MessagesList>
<DirectChatNewMessage
chatId={props?.chat?._id}
chat={props?.chat}
chatId={props?.chat?.chat?._id}
refreshChat={handleRefresh}
interlucator={props.interlucator}
/>
</DirectChatContentContainer>
)}

+ 14
- 6
src/components/DirectChat/DirectChatContent/DirectChatContentHeader/DirectChatContentHeader.js Bestand weergeven

@@ -14,22 +14,30 @@ import {
} from "./DirectChatContentHeader.styled";
import PopoverComponent from "../../../Popovers/PopoverComponent";
import PhonePopover from "../../../Popovers/PhonePopover/PhonePopover";
import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter";
import useIsMobile from "../../../../hooks/useIsMobile";

const DirectChatContentHeader = (props) => {
const [showPhonePopover, setShowPhonePopover] = useState(false);
const [phonePopoverAnchorEl, setPhonePopoverAnchorEl] = useState(null);
const { isMobile } = useIsMobile();

const togglePhonePopover = (event) => {
console.log(event);
setShowPhonePopover(true);
setPhonePopoverAnchorEl(event.currentTarget);
}
setShowPhonePopover(true);
setPhonePopoverAnchorEl(event.currentTarget);
};

return (
<DirectChatContentHeaderContainer>
<DirectChatContentHeaderFlexContainer>
<ProfileImage src={props?.interlucator?.image} />
{/* <ProfileImage src={props?.interlucator?.image} /> */}
<ProfileImage
src={getImageUrl(
props?.interlucator?.image,
variants.chatHeader,
isMobile
)}
/>
<ProfileDetails>
<ProfileName>{props?.interlucator?.name}</ProfileName>
<ProfileLocation>

+ 94
- 21
src/components/DirectChat/DirectChatNewMessage/DirectChatNewMessage.js Bestand weergeven

@@ -8,40 +8,106 @@ 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 {
fetchChats,
sendMessage,
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("");
const [isFocused, setIsFocused] = useState(false);
const exchange = useSelector(selectExchange);
const dispatch = useDispatch();
const { t } = useTranslation();
const location = useLocation();
const history = useHistory();
const handleApiResponseSuccess = () => {
props.refreshChat();
};
const handleSend = useCallback(() => {
if (location.state?.offerId) {
initiateNewChat(typedValue);
} else {
dispatch(
sendMessage({
message: typedValue,
chatId: props.chatId,
handleApiResponseSuccess,
})
);
}
setTypedValue("");
}, [typedValue]);
// 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;
sendMessage(chatId, userId, typedValue, props.interlucator.userId);
dispatch(
addNewMessage({
_id: chatId,
message: {
userId,
text: typedValue,
_created: new Date().toISOString(),
},
})
);
if (props.chatId) {
if (!exchange.valid && props.chat?.offer?.offer?.userId === userId) {
dispatch(validateExchange(exchange._id));
}
}
} 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]
);
const handleMessageSendSuccess = (newChatId) => {
history.replace(`${newChatId}`);
dispatch(fetchChats());
// dispatch(fetchChats());
// sendMessage(newChatId, userId, typedValue, props.interlucator.userId);
// dispatch(
// addNewMessage({
// _id: newChatId,
// message: {
// userId,
// text: typedValue,
// _created: new Date().toISOString(),
// },
// })
// );
// handleSend(newChatId);
};

useEffect(() => {
@@ -55,7 +121,12 @@ const DirectChatNewMessage = (props) => {
const initiateNewChat = (typedValue) => {
const offerId = location.state.offerId;
dispatch(
startNewChat({ offerId, message: typedValue, handleMessageSendSuccess })
startNewChat({
offerId,
message: typedValue,
interlucatorUserId: props.interlucator.userId,
handleMessageSendSuccess,
})
);
};
return (
@@ -84,6 +155,8 @@ DirectChatNewMessage.propTypes = {
children: PropTypes.node,
chatId: PropTypes.any,
refreshChat: PropTypes.func,
interlucator: PropTypes.any,
chat: PropTypes.any,
};

export default DirectChatNewMessage;

+ 0
- 1
src/components/Header/AboutHeader/AboutHeader.js Bestand weergeven

@@ -8,7 +8,6 @@ import { selectAboutRouteSelected } from "../../../store/selectors/appSelectors"
import { useHistory } from "react-router-dom";

const AboutHeader = () => {
console.log("about header");
const history = useHistory();
const { t } = useTranslation();
const aboutRouteSelected = useSelector(selectAboutRouteSelected);

+ 1
- 1
src/components/Header/Header.js Bestand weergeven

@@ -313,7 +313,7 @@ const Header = () => {
color: selectedTheme.primaryPurple,
}}
>
<Badge badgeContent={3} color="primary">
<Badge color="primary">
<MailIcon />
</Badge>
</IconButton>

+ 31
- 14
src/components/ImagePicker/ImagePicker.js Bestand weergeven

@@ -11,6 +11,7 @@ import {
import { IconButton } from "../Buttons/IconButton/IconButton";
import { ReactComponent as EditIcon } from "../../assets/images/svg/edit.svg";
import { ReactComponent as TrashIcon } from "../../assets/images/svg/trash.svg";
import { getImageUrl, variants } from "../../util/helpers/imageUrlGetter";

const ImagePicker = (props) => {
const fileInputRef = useRef(null);
@@ -19,19 +20,27 @@ const ImagePicker = (props) => {
const [isEditing, setIsEditing] = useState(false);

useEffect(() => {
if (props.image)
setImage(props.image);
}, [props.image]);

let listener = useCallback((event) => {
if (imageRef.current) {
if (imageRef.current.contains(event.target)) {
setIsEditing(true);
if (props.image) {
if (typeof props.image === 'string') {
setImage(getImageUrl(props.image, variants.offerCard))
} else {
setIsEditing(false);
handleImage(props.image);
}
}
}, [imageRef.current])
}, [props.image]);

let listener = useCallback(
(event) => {
if (imageRef.current) {
if (imageRef.current.contains(event.target)) {
setIsEditing(true);
} else {
setIsEditing(false);
}
}
},
[imageRef.current]
);
useEffect(() => {
window.addEventListener("click", listener);
return () => window.removeEventListener("click", listener);
@@ -40,11 +49,12 @@ const ImagePicker = (props) => {
fileInputRef.current.value = "";
fileInputRef.current.click();
};
const handleImage = (event) => {
const handleImage = (file) => {
let reader = new FileReader();
reader.readAsDataURL(event.target.files[0]);
reader.readAsDataURL(file);
// reader.readAsBinaryString(file);
reader.onload = () => {
if (props.setImage) props.setImage(reader.result);
if (props.setImage) props.setImage(file);
setImage(reader.result);
};
reader.onerror = (error) => {
@@ -61,8 +71,15 @@ const ImagePicker = (props) => {
className={props.className}
onClick={!image ? handleChange : () => {}}
hasImage={props.image}
component="form"
>
<AddFile type="file" ref={fileInputRef} onInput={handleImage} accept=".jpg, .jpeg, .png" />
<AddFile
type="file"
ref={fileInputRef}
onInput={(event) => handleImage(event.target.files[0])}
accept=".jpg, .jpeg, .png"
formEncType="multipart/form-data"
/>
{image ? (
<React.Fragment>
<ImageUploaded src={image} draggable={false} ref={imageRef} />

+ 11
- 1
src/components/ItemDetails/ItemDetailsHeaderCard/ItemDetailsHeaderCard.js Bestand weergeven

@@ -18,12 +18,15 @@ import { selectUserId } from "../../../store/selectors/loginSelectors";
import StatisticDetails from "./StatisticDetails/StatisticDetails";
import PIBDetail from "./OfferDetail/PIB/PIBDetail";
import CategoryDetail from "./OfferDetail/Category/CategoryDetail";
import useIsMobile from "../../../hooks/useIsMobile";
import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter";

const ItemDetailsHeaderCard = (props) => {
const history = useHistory();
const chats = useSelector(selectLatestChats);
const offer = props.offer;
const userId = useSelector(selectUserId);
const { isMobile } = useIsMobile();

const handleGoProfile = () => {
history.push(`/profile/${offer?.offer?.userId}`);
@@ -48,7 +51,14 @@ const ItemDetailsHeaderCard = (props) => {
halfwidth={props.halfwidth ? 1 : 0}
>
<HeaderTop>
<OfferImage src={offer?.companyData?.image} />
{/* <OfferImage src={offer?.companyData?.image} /> */}
<OfferImage
src={getImageUrl(
offer?.companyData?.image,
variants.profileImage,
isMobile
)}
/>
<OfferDetails>
<OfferTitle isMyProfile={props.isMyProfile} onClick={handleGoProfile}>
{offer?.companyData?.company?.name}

+ 1
- 1
src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/Category/CategoryDetail.js Bestand weergeven

@@ -15,7 +15,7 @@ const CategoryDetail = (props) => {
<DetailIcon color={selectedTheme.iconStrokeColor} size="22px">
<Category width={"22px"} />
</DetailIcon>
<DetailText isMyProfile={props.isMyProfile}>
<DetailText ismyprofile={props.isMyProfile}>
{offer?.companyData?.company?.contacts?.location}
</DetailText>
</DetailContainer>

+ 1
- 1
src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/Category/CategoryDetail.styled.js Bestand weergeven

@@ -27,7 +27,7 @@ export const DetailIcon = styled(Icon)`
`;
export const DetailText = styled(Typography)`
font-family: "DM Sans";
color: ${props => props.isMyProfile ? "white" : selectedTheme.primaryText};
color: ${props => props.ismyprofile ? "white" : selectedTheme.primaryText};
line-height: 16px;
font-size: 16px;
position: relative;

+ 1
- 4
src/components/Login/Login.js Bestand weergeven

@@ -62,9 +62,7 @@ const Login = () => {

const handleSubmit = (values) => {
const { email, password } = values;
if (!formik.isValid) {
console.log("invalid");
} else {
if (formik.isValid) {
dispatch(clearLoginErrors());
dispatch(
fetchLogin({
@@ -74,7 +72,6 @@ const Login = () => {
handleApiResponseError,
})
);
console.log(values);
}
};


+ 1
- 0
src/components/MarketPlace/Offers/OffersNotFound.styled.js Bestand weergeven

@@ -10,6 +10,7 @@ export const OffersNotFoundContainer = styled(Box)`
align-items: center;
justify-content: center;
height: 70vh;
text-align: center;
`;

export const OffersNotFoundHeading = styled(Typography)`

+ 47
- 32
src/components/Popovers/HeaderPopover/HeaderPopover.js Bestand weergeven

@@ -17,45 +17,60 @@ import {
SecondaryTextContainer,
} from "./HeaderPopover.styled";
import { useTranslation } from "react-i18next";
import useIsMobile from "../../../hooks/useIsMobile";
import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter";
import { useMemo } from "react";

const HeaderPopover = (props) => {
const { t } = useTranslation();
const { isMobile } = useIsMobile();
const items = useMemo(() => props.items, [props.items]);
return (
<HeaderPopoverContainer>
<PopoverTitle p={2}>{props.title}</PopoverTitle>
<PopoverList>
{props.items?.length > 0 ? (
props.items.map((item, index) => (
<PopoverListItem key={index}>
<PopoverListItemAvatarContainer>
{props.isProfile ? (
<PopoverListItemProfileAvatar
alt={item.alt}
src={item.src}
onClick={item?.onClick}
/>
) : (
<PopoverListItemAvatar
alt={item.alt}
src={item.src}
onClick={item?.onClick}
/>
)}
</PopoverListItemAvatarContainer>
<PopoverListItemTextContainer
primaryTypographyProps={{
onClick: item.onClick,
}}
primary={item.title}
secondary={
<SecondaryTextContainer>
<SecondaryText>{item.text}</SecondaryText>
<NameOfProduct>{item?.bigText}</NameOfProduct>
</SecondaryTextContainer>
}
></PopoverListItemTextContainer>
</PopoverListItem>
))
{items?.length > 0 ? (
items.map((item, index) => {
return (
<PopoverListItem key={index}>
<PopoverListItemAvatarContainer>
{props.isProfile ? (
<PopoverListItemProfileAvatar
alt={item.alt}
src={getImageUrl(
item.src,
variants.profileCard,
isMobile
)}
onClick={item?.onClick}
/>
) : (
<PopoverListItemAvatar
alt={item.alt}
src={getImageUrl(
item.src,
variants.profileCard,
isMobile
)}
onClick={item?.onClick}
/>
)}
</PopoverListItemAvatarContainer>
<PopoverListItemTextContainer
primaryTypographyProps={{
onClick: item.onClick,
}}
primary={item.title}
secondary={
<SecondaryTextContainer>
<SecondaryText>{item.text}</SecondaryText>
<NameOfProduct>{item?.bigText}</NameOfProduct>
</SecondaryTextContainer>
}
></PopoverListItemTextContainer>
</PopoverListItem>
);
})
) : (
<PopoverNoItemsText>{t("header.noItems")}</PopoverNoItemsText>
)}

+ 9
- 4
src/components/Popovers/MyMessages/MyMessages.js Bestand weergeven

@@ -7,6 +7,7 @@ import { selectLatestChats } from "../../../store/selectors/chatSelectors";
import { selectUserId } from "../../../store/selectors/loginSelectors";
import HeaderPopover from "../HeaderPopover/HeaderPopover";
import PropTypes from "prop-types";
import { makeErrorToastMessage } from "../../../store/utils/makeToastMessage";

export const MyMessages = (props) => {
const { t } = useTranslation();
@@ -17,14 +18,13 @@ export const MyMessages = (props) => {
const [lastChats, setLastChats] = useState([]);

const convertMessages = (messages) => {
console.log(messages)
return messages
.map((item) => ({
src: item.interlocutorData.image,
title: item.interlocutorData.name,
onClick: () => goToMessage(item?.chat?._id),
text: "Proizvod: ",
bigText: item.offerData.name
bigText: item.offerData.name,
}))
.slice(0, 2);
};
@@ -40,8 +40,13 @@ export const MyMessages = (props) => {
}
}, [chats]);
const goToMessages = () => {
history.push(`/messages/${chats[0].chat?._id}`);
props.closePopover();
if (lastChats.length !== 0) {
history.push(`/messages/${chats[0].chat?._id}`);
props.closePopover();
} else {
makeErrorToastMessage(t("messages.noMessagesToast"));
props.closePopover();
}
};
const goToMessage = (chatId) => {
history.push(`/messages/${chatId}`);

+ 35
- 32
src/components/Popovers/MyPosts/MyPosts.js Bestand weergeven

@@ -24,6 +24,7 @@ import { fetchMineOffers } from "../../../store/actions/offers/offersActions";
import { selectProfileName } from "../../../store/selectors/profileSelectors";
import { useHistory } from "react-router-dom";
import { MY_OFFERS_PAGE } from "../../../constants/pages";
import { useMemo } from "react";

export const MyPosts = (props) => {
const { t } = useTranslation();
@@ -36,40 +37,42 @@ export const MyPosts = (props) => {
dispatch(fetchMineOffers());
}, []);
useEffect(() => {
if (mineOffers?.length > 0) {
if (mineOffers.length > 1) {
setArrayOfMineOffers(
[mineOffers[0], mineOffers[1]].map((item) => ({
src: item.images[0],
title: item.name,
onClick: () => goToOffer(item._id),
text: (
<React.Fragment>
<PostsImgSuit /> {name}
</React.Fragment>
),
}))
);
} else if (mineOffers.length > 0) {
setArrayOfMineOffers(
[mineOffers[0]].map((item) => ({
alt: "Photo",
src: item.images[0],
title: item.name,
onClick: () => goToOffer(item._id),
text: (
<React.Fragment>
<PostsImgSuit /> {name}
</React.Fragment>
),
}))
);
} else {
setArrayOfMineOffers([]);
}
if (mineOffers.length > 1) {
setArrayOfMineOffers(
[mineOffers[0], mineOffers[1]].map((item) => ({
src: item.images[0],
title: item.name,
onClick: () => goToOffer(item._id),
text: (
<React.Fragment>
<PostsImgSuit /> {name}
</React.Fragment>
),
}))
);
} else if (mineOffers.length > 0) {
setArrayOfMineOffers(
[mineOffers[0]].map((item) => ({
alt: "Photo",
src: item.images[0],
title: item.name,
onClick: () => goToOffer(item._id),
text: (
<React.Fragment>
<PostsImgSuit /> {name}
</React.Fragment>
),
}))
);
} else {
setArrayOfMineOffers([]);
}
}, [mineOffers]);

const mineOffersToRender = useMemo(() => {
return [...arrayOfMineOffers];
}, [arrayOfMineOffers, mineOffers]);

const goToOffer = (id) => {
history.push(`/proizvodi/${id}`);
props.closePopover();
@@ -81,7 +84,7 @@ export const MyPosts = (props) => {
return (
<HeaderPopover
title={t("header.myOffers")}
items={arrayOfMineOffers}
items={mineOffersToRender}
buttonText={t("header.checkEverything")}
buttonOnClick={goToMySwaps}
/>

+ 0
- 1
src/components/Profile/ProfileOffers/ProfileOffers.js Bestand weergeven

@@ -37,7 +37,6 @@ const ProfileOffers = (props) => {
const isLoadingMineOffers = useSelector(
selectIsLoadingByActionType(OFFERS_PROFILE_SCOPE)
);
console.log("isLoadingMineOffers", isLoadingMineOffers);
const searchRef = useRef(null);
const chats = useSelector(selectLatestChats);
const profileOffers = useSelector(selectProfileOffers);

+ 16
- 4
src/components/ProfileCard/EditProfile/EditProfile.js Bestand weergeven

@@ -15,6 +15,7 @@ import {
ButtonsContainer,
ErrorMessage,
ProfileImagePicker,
InputFieldLabelLocation,
} from "./EditProfile.styled";
import selectedTheme from "../../../themes";
import { useFormik } from "formik";
@@ -29,6 +30,8 @@ import { useDispatch, useSelector } from "react-redux";
import { selectUserId } from "../../../store/selectors/loginSelectors";
import editProfileValidation from "../../../validations/editProfileValidation";
import useIsMobile from "../../../hooks/useIsMobile";
import AutoSuggestTextField from "../../TextFields/AutoSuggestTextField/AutoSuggestTextField";
import { selectLocations } from "../../../store/selectors/locationsSelectors";

const EditProfile = (props) => {
const [profileImage, setProfileImage] = useState(props.profile.image);
@@ -38,6 +41,7 @@ const EditProfile = (props) => {
const dispatch = useDispatch();
const { isMobile } = useIsMobile();
const userId = useSelector(selectUserId);
const locations = useSelector(selectLocations);

useEffect(() => {
if (isMobile) {
@@ -53,7 +57,6 @@ const EditProfile = (props) => {
};

const handleSubmit = (values) => {
console.log(values);
dispatch(editMineProfile({ ...values, handleApiResponseSuccess }));
props.closeModalHandler();
};
@@ -132,23 +135,32 @@ const EditProfile = (props) => {
fullWidth
disabled
/>
<InputFieldLabel
<InputFieldLabelLocation
leftText={t("common.labelLocation").toUpperCase()}
/>
<InputField
<AutoSuggestTextField
editLocation
data={locations.map((item) => ({ name: item.city }))}
value={formik.values.firmLocation}
onChange={(event, { newValue }) =>
formik.setFieldValue("firmLocation", newValue)
}
/>
{/* <InputField
name="firmLocation"
value={formik.values.firmLocation}
onChange={formik.handleChange}
error={formik.touched.firmLocation && formik.errors.firmLocation}
margin="normal"
fullWidth
/>
/> */}
</BasicInfo>
)}
{showDetails && (
<DetailsInfo>
<InputFieldLabel
leftText={t("editProfile.website").toUpperCase()}
labelWebsite
/>
<InputField
name="firmWebsite"

+ 26
- 1
src/components/ProfileCard/EditProfile/EditProfile.styled.js Bestand weergeven

@@ -86,6 +86,7 @@ export const CloseButton = styled(Box)`
export const InputFieldLabel = styled(Label)`
position: relative;
bottom: -14px;

& label {
font-size: 12px;
font-weight: 600;
@@ -93,12 +94,13 @@ export const InputFieldLabel = styled(Label)`
color: #808080;
cursor: auto;
letter-spacing: 0.2px;
${(props) => props.labelWebsite && `margin-top: 8px`}
}

@media screen and (max-width: 600px) {
& label {
font-size: 9px;
margin-top: -3px;
margin-top: 0;
}
}
`;
@@ -117,6 +119,29 @@ export const InputField = styled(TextField)`
}
`;

export const InputFieldLabelLocation = styled(Label)`
position: relative;
bottom: -8px;
margin: 10px 0;
& label {
font-size: 12px;
font-weight: 600;
line-height: 20px;
color: #808080;
cursor: auto;
letter-spacing: 0.2px;
}

@media screen and (max-width: 600px) {
bottom: -12px;
margin: 5px 0 17px 0;
& label {
font-size: 9px;
margin-top: 0;
}
}
`;

export const SaveButton = styled(PrimaryButton)`
font-size: 12px;
letter-spacing: 1.5px;

+ 11
- 1
src/components/ProfileCard/ProfileMainInfo/ProfileMainInfo.js Bestand weergeven

@@ -11,15 +11,25 @@ import {
ProfilePIB,
} from "./ProfileMainInfo.styled";
import { useTranslation } from "react-i18next";
import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter";
import useIsMobile from "../../../hooks/useIsMobile";

const ProfileMainInfo = (props) => {
const { t } = useTranslation();
const { isMobile } = useIsMobile();
return (
<ProfileMainInfoContainer>
<AvatarImageContainer>
<AvatarImage
{/* <AvatarImage
alt={props.profile?.company?.name}
src={props.profile?.image}
/> */}
<AvatarImage
src={getImageUrl(
props.profile?.image,
variants.profileImage,
isMobile
)}
/>
</AvatarImageContainer>
<ProfileMainInfoGrid>

+ 6
- 5
src/components/TextFields/AutoSuggestTextField/AutoSuggestTextField.js Bestand weergeven

@@ -12,18 +12,18 @@ const AutoSuggestTextField = (props) => {

const getSuggestions = (value) => {
const escapedValue = escapeRegexCharacters(value.trim());
if (escapedValue === "") {
return [];
}
const regex = new RegExp("^" + escapedValue, "i");
const suggestions = data.filter((dataItem) => regex.test(dataItem.name));
if (suggestions.length === 0) {
return [{ isAddNew: true }];
}
return suggestions;
};

@@ -49,7 +49,7 @@ const AutoSuggestTextField = (props) => {
};

return (
<AutoSuggestTextFieldContainer>
<AutoSuggestTextFieldContainer editLocation={props.editLocation}>
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
@@ -68,6 +68,7 @@ AutoSuggestTextField.propTypes = {
value: PropTypes.string,
onChange: PropTypes.func,
data: PropTypes.array,
editLocation: PropTypes.bool,
};

export default AutoSuggestTextField;

+ 11
- 3
src/components/TextFields/AutoSuggestTextField/AutoSuggestTextField.styled.js Bestand weergeven

@@ -5,12 +5,12 @@ import selectedTheme from "../../../themes";
export const AutoSuggestTextFieldContainer = styled(Box)`
& .react-autosuggest__container {
width: 100%;
height: 48px;
height: ${(props) => (props.editLocation ? "43px" : "48px")};
position: relative;
z-index: 20;
& input {
width: 100%;
height: 48px;
height: ${(props) => (props.editLocation ? "43px" : "48px")};
padding: 4px 14px;
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.23);
@@ -27,7 +27,7 @@ export const AutoSuggestTextFieldContainer = styled(Box)`
border: 2px solid ${selectedTheme.primaryPurple};
}
& input::placeholder {
color: rgba(0,0,0, 0.38);
color: rgba(0, 0, 0, 0.38);
}

& div {
@@ -56,4 +56,12 @@ export const AutoSuggestTextFieldContainer = styled(Box)`
color: ${selectedTheme.primaryBackgroundColor};
}
}

@media (max-width: 600px) {
& .react-autosuggest__container {
& input {
font-size: 13px;
}
}
}
`;

+ 0
- 1
src/components/UserReviews/UserReviews.js Bestand weergeven

@@ -31,7 +31,6 @@ const UserReviews = (props) => {
const dispatch = useDispatch();

useEffect(() => {
console.log(routeMatch);
if (props.profileReviews && routeMatch.params?.idProfile) {
let idProfile = routeMatch.params.idProfile;
dispatch(fetchReviews(idProfile));

+ 0
- 3
src/hooks/useOffers/useSearch.js Bestand weergeven

@@ -33,7 +33,6 @@ const useSearch = (applyAllFilters) => {
clear();
}
const queryObject = new URLSearchParams(history.location.search);
console.log(queryObject.toString())
if (queryObject.has(KEY_SEARCH)) {
searchOffers(queryObject.get(KEY_SEARCH));
} else {
@@ -43,7 +42,6 @@ const useSearch = (applyAllFilters) => {

// On every local change of search string, global state of search string should be also updated
useEffect(() => {
console.log('ovde')
if (isInitallyLoaded && applyAllFilters) {
dispatch(setSearchString(searchStringLocally));
}
@@ -55,7 +53,6 @@ const useSearch = (applyAllFilters) => {
};

const clear = () => {
console.log('ovde2')
setSearchStringLocally("");
};


+ 1
- 0
src/i18n/resources/rs.js Bestand weergeven

@@ -208,6 +208,7 @@ export default {
send: "Pošalji",
sendPlaceholder: "Poruka...",
seeChats: "Pogledaj ćaskanje",
noMessagesToast: "Nemate ni jednu poruku!"
},
editProfile: {
website: "Web Sajt*",

+ 3
- 5
src/pages/About/AboutPage.js Bestand weergeven

@@ -40,14 +40,13 @@ const AboutPage = () => {
const yAxis = privacyPolicyRef.current.offsetTop - 64;
window.scrollTo({ top: yAxis, behavior: "smooth" });
if (aboutRouteSelected !== scrollConstants.about.privacyPolicyPage) {
dispatch(setAboutRouteSelected(scrollConstants.about.privacyPolicyPage));
dispatch(
setAboutRouteSelected(scrollConstants.about.privacyPolicyPage)
);
}
}
location.state = {};
}
return () => {
console.log(location);
};
}, [location]);

useEffect(() => {
@@ -56,7 +55,6 @@ const AboutPage = () => {
window.scrollY >
pricesRef.current.offsetTop - window.innerHeight / 2
) {
console.log(true);
if (
window.scrollY >
privacyPolicyRef.current.offsetTop - window.innerHeight / 2

+ 0
- 1
src/pages/ItemDetailsPage/ItemDetailsPageMUI.js Bestand weergeven

@@ -21,7 +21,6 @@ const ItemDetailsPage = (props) => {
}, []);

useEffect(() => {
console.log("effect: selectedOffer", selectedOffer, " isInitiallyLoaded: ", isInitiallyLoaded)
if (!selectedOffer?.offer && isInitiallyLoaded) {
dispatch(fetchOneOffer(offerId));
}

+ 0
- 1
src/pages/RegisterPages/Register/FirstPart/FirstPartOfRegistration.js Bestand weergeven

@@ -40,7 +40,6 @@ const FirstPartOfRegistration = (props) => {

const handleSubmitForm = (event) => {
event.preventDefault();
console.log(formik);
if (!formik.isValid) {
if (formik.errors.mail) {
formik.setFieldValue("mail", "");

+ 0
- 1
src/pages/RegisterPages/Register/Register.js Bestand weergeven

@@ -71,7 +71,6 @@ const Register = () => {
}
};


const registerUser = (values) => {
dispatch(
fetchRegisterUser({ values, handleResponseSuccess, handleResponseError })

+ 32
- 0
src/socket/socket.js Bestand weergeven

@@ -0,0 +1,32 @@
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) => {
socket.auth = {
userId,
};
};

export const sendMessage = (chatId, userId, text, receiverUserId) => {
console.log("CHATID: ", chatId);
socket.emit("private_message", {
chatId,
receiverUserId,
message: {
userId,
text,
},
});
};

export const addMesageListener = (listener) => {
return socket.on("private_message", listener);
};
export const removeMessageListener = () => {
return socket.off("private_message");
};

+ 1
- 0
src/store/actions/chat/chatActionConstants.js Bestand weergeven

@@ -34,4 +34,5 @@ export const CHAT_NEW_FETCH_ERROR = createErrorType(CHAT_NEW_SCOPE);
export const CHAT_SET = createSetType("CHAT_SET");
export const CHAT_ONE_SET = createSetType("CHAT_ONE_SET");
export const CHAT_CLEAR = createSetType("CHAT_CLEAR");
export const CHAT_ADD_MESSAGE = createSetType("CHAT_ADD_MESSAGE");
// export const ADD_ONE_CHAT = "CHAT_ONE_ADD";

+ 5
- 1
src/store/actions/chat/chatActions.js Bestand weergeven

@@ -1,4 +1,4 @@
import { CHAT_CLEAR, CHAT_FETCH, CHAT_FETCH_ERROR, CHAT_FETCH_SUCCESS, CHAT_HEADER_FETCH, CHAT_HEADER_FETCH_ERROR, CHAT_HEADER_FETCH_SUCCESS, CHAT_NEW_FETCH, CHAT_NEW_FETCH_ERROR, CHAT_NEW_FETCH_SUCCESS, CHAT_ONE_FETCH, CHAT_ONE_FETCH_ERROR, CHAT_ONE_FETCH_SUCCESS, CHAT_ONE_SET, CHAT_SEND_ERROR, CHAT_SEND_FETCH, CHAT_SEND_SUCCESS, CHAT_SET } from "./chatActionConstants";
import { CHAT_ADD_MESSAGE, CHAT_CLEAR, CHAT_FETCH, CHAT_FETCH_ERROR, CHAT_FETCH_SUCCESS, CHAT_HEADER_FETCH, CHAT_HEADER_FETCH_ERROR, CHAT_HEADER_FETCH_SUCCESS, CHAT_NEW_FETCH, CHAT_NEW_FETCH_ERROR, CHAT_NEW_FETCH_SUCCESS, CHAT_ONE_FETCH, CHAT_ONE_FETCH_ERROR, CHAT_ONE_FETCH_SUCCESS, CHAT_ONE_SET, CHAT_SEND_ERROR, CHAT_SEND_FETCH, CHAT_SEND_SUCCESS, CHAT_SET } from "./chatActionConstants";

export const fetchChats = (payload) => ({
type: CHAT_FETCH,
@@ -60,4 +60,8 @@ export const sendMessageError = () => ({
})
export const startNewChatError = () => ({
type: CHAT_NEW_FETCH_ERROR
})
export const addNewMessage = (payload) => ({
type: CHAT_ADD_MESSAGE,
payload
})

+ 4
- 1
src/store/middleware/accessTokensMiddleware.js Bestand weergeven

@@ -16,6 +16,8 @@ import { logoutUser, refreshUserToken } from "../actions/login/loginActions";
// const baseURL = "http://192.168.88.143:3001/"; // DULE
// const baseURL = "http://192.168.88.175:3005/";
const baseURL = "https://trampa-api-test.dilig.net/";
// const baseURL = "http://192.168.88.150:3001/"; // DJOLE
// const baseURL = "http://localhost:3001/";

//Interceptor unique name
export const accessTokensMiddlewareInterceptorName = "ACCESS_TOKEN_INTERCEPTOR";
@@ -42,7 +44,8 @@ export default ({ dispatch }) =>
const axiosResponse = await axios.post(`${baseURL}auth/refresh`, {
token: refresh,
});
const newToken = axiosResponse.data.token;
const newToken = axiosResponse.data;

response.headers.Authorization = `Bearer ${newToken}`;
dispatch(refreshUserToken(newToken));
}

+ 61
- 22
src/store/reducers/chat/chatReducer.js Bestand weergeven

@@ -1,32 +1,71 @@
import { CHAT_CLEAR, CHAT_ONE_SET, CHAT_SET } from "../../actions/chat/chatActionConstants"
import createReducer from "../../utils/createReducer"
import {
CHAT_ADD_MESSAGE,
CHAT_CLEAR,
CHAT_ONE_SET,
CHAT_SET,
} from "../../actions/chat/chatActionConstants";
import createReducer from "../../utils/createReducer";

const initialState = {
latestChats: [],
selectedChat: {}
}
latestChats: [],
selectedChat: {},
};

export default createReducer(
{
[CHAT_SET]: setChats,
[CHAT_ONE_SET]: setOneChat,
[CHAT_CLEAR]: clearChats
},
initialState
)
{
[CHAT_SET]: setChats,
[CHAT_ONE_SET]: setOneChat,
[CHAT_CLEAR]: clearChats,
[CHAT_ADD_MESSAGE]: addNewMessage,
},
initialState
);

function setChats(state, action) {
return {
...state,
latestChats: [...action.payload]
}
return {
...state,
latestChats: [...action.payload],
};
}
function setOneChat(state, action) {
return {
...state,
selectedChat: action.payload
}
return {
...state,
selectedChat: action.payload,
};
}
function clearChats() {
return initialState;
}
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) {
chat = {
...chat,
chat: {
...chat.chat,
messages: [...chat.chat.messages, payload.message],
},
};
allChats = allChats.filter((item) => item.chat._id !== chat.chat._id);
allChats = [chat, ...allChats];
}
let newSelectedChat = {};
if (state.selectedChat.chat) {
newSelectedChat = { ...state.selectedChat };
if (newSelectedChat.chat._id === chat.chat._id)
newSelectedChat = { ...newSelectedChat, chat: chat.chat };
} else {
newSelectedChat = { ...chat };
}
console.log("chat", chat)
console.log("allChats", allChats);
console.log("newSelectedChat", newSelectedChat);
return {
...state,
latestChats: [...allChats],
selectedChat: { ...newSelectedChat },
};
}

+ 38
- 5
src/store/saga/chatSaga.js Bestand weergeven

@@ -14,11 +14,26 @@ import {
CHAT_ONE_FETCH,
CHAT_SEND_FETCH,
} from "../actions/chat/chatActionConstants";
import { fetchChatsError, fetchChatsSuccess, fetchHeaderChatsError, fetchHeaderChatsSuccess, fetchOneChatError, fetchOneChatSuccess, sendMessageError, sendMessageSuccess, setChats, setOneChat, startNewChatError, startNewChatSuccess } from "../actions/chat/chatActions";
import {
addNewMessage,
fetchChatsError,
fetchChatsSuccess,
fetchHeaderChatsError,
fetchHeaderChatsSuccess,
fetchOneChatError,
fetchOneChatSuccess,
sendMessageError,
sendMessageSuccess,
setChats,
setOneChat,
startNewChatError,
startNewChatSuccess,
} from "../actions/chat/chatActions";
import { validateExchange } from "../actions/exchange/exchangeActions";
import { selectSelectedChat } from "../selectors/chatSelectors";
import { selectExchange } from "../selectors/exchangeSelector";
import { selectUserId } from "../selectors/loginSelectors";
import { sendMessage as SendMessageSocket } from "../../socket/socket";

function* fetchChats() {
try {
@@ -91,12 +106,30 @@ function* startNewChat(payload) {
attemptCreateNewChat,
payload.payload.offerId
);
const userId = yield select(selectUserId);
const data = yield call(attemptFetchChats, userId);
yield put(setChats([...data.data]));
console.log(newChatData);
const newChatId = newChatData.data.chatId;
const messageObject = {
text: payload.payload.message,
};
yield call(attemptSendMessage, newChatId, messageObject);
yield put(startNewChatSuccess());
console.log(payload);
yield call(
SendMessageSocket,
newChatData.data.chatId,
userId,
payload.payload.message,
payload.payload.interlucatorUserId
);
yield put(
addNewMessage({
_id: newChatId,
message: {
userId,
text: payload.payload.message,
_created: new Date().toISOString(),
},
})
);
if (payload.payload.handleMessageSendSuccess) {
yield call(payload.payload.handleMessageSendSuccess, newChatId);
}

+ 1
- 1
src/store/saga/loginSaga.js Bestand weergeven

@@ -71,7 +71,7 @@ function* fetchLogin({ payload }) {
}
console.log(e.response.status);
let errorMessage = yield call(rejectErrorCodeHelper, e.response.status);
if (e.response.status === 400 || e.response.status === 404) {
if (e.response.status === 401 || e.response.status === 404) {
errorMessage = i18next.t("login.wrongCredentials", {
lng: "rs"
});

+ 43
- 5
src/store/saga/offersSaga.js Bestand weergeven

@@ -127,8 +127,10 @@ function* fetchOffers(payload) {
"?" + newQueryString.toString()
);
yield put(setTotalOffers(data.data.total));
yield put(setOffers(data.data.offers.filter(offer => !offer.pinned)));
yield put(setPinnedOffers(data.data.offers.filter(offer => offer.pinned)));
yield put(setOffers(data.data.offers.filter((offer) => !offer.pinned)));
yield put(
setPinnedOffers(data.data.offers.filter((offer) => offer.pinned))
);
yield put(fetchOffersSuccess());
} catch (e) {
yield put(fetchOffersError());
@@ -189,7 +191,19 @@ function* fetchMoreOffers(payload) {

function* createOffer(payload) {
try {
yield call(attemptAddOffer, payload.payload.values.offerData);
const offerData = payload.payload.values.offerData;
const formData = new FormData();
formData.append("category[name]", offerData.category.name);
formData.append("condition", offerData.condition);
formData.append("description", offerData.description);
// formData.append("file", JSON.stringify(offerData.images))
for (var i = 0; i < offerData.images.length; i++) {
formData.append("file", offerData.images[i]);
}
formData.append("location[city]", offerData.location.city);
formData.append("name", offerData.name);
formData.append("subcategory", offerData.subcategory);
yield call(attemptAddOffer, formData);
yield put(addOfferSuccess());
if (payload.payload.handleApiResponseSuccess) {
yield call(payload.payload.handleApiResponseSuccess);
@@ -260,8 +274,32 @@ function* removeOffer(payload) {
function* editOffer(payload) {
try {
const offerId = payload.payload.offerId;
const editedData = payload.payload.offerData;
yield call(attemptEditOffer, offerId, editedData);
// const editedData = payload.payload.offerData;
const offerData = payload.payload.offerData;
const formData = new FormData();
formData.append("category[name]", offerData.category.name);
formData.append("condition", offerData.condition);
formData.append("description", offerData.description);
// const oldImages = [];
for (var i = 0; i < offerData.images.length; i++) {
if (offerData.images[i] !== null) {
if (typeof offerData.images[i] === "string") {
formData.append("images[]", offerData.images[i]);
} else {
formData.append("file", offerData.images[i]);
}
}
}
// if (oldImages.length > 0) {
// formData.append("images", JSON.stringify(oldImages));
// }
// if (oldImages.length === offerData.images.length) {
// formData.append("file", "");
// }
formData.append("location[city]", offerData.location.city);
formData.append("name", offerData.name);
formData.append("subcategory", offerData.subcategory);
yield call(attemptEditOffer, offerId, formData);
yield put(editOfferSuccess());
if (payload.payload.handleApiResponseSuccess) {
yield call(payload.payload.handleApiResponseSuccess);

+ 45
- 30
src/store/saga/profileSaga.js Bestand weergeven

@@ -46,44 +46,59 @@ function* fetchMineProfile() {

function* changeMineProfile(payload) {
try {
let image;
if (payload.payload.firmLogo.includes("data:image")) {
image = payload.payload.firmLogo
.replace("data:image/jpeg;base64,", "")
.replace("data:image/jpg;base64,", "")
.replace("data:image/png;base64,", "");
} else if (payload.payload.firmLogo === "") {
image = "";
}
// console.log(payload);
// let image;
// if (payload.payload.firmLogo) {
// image = payload.payload.firmLogo;
// } else if (payload.payload.firmLogo === "") {
// image = "";
// }

// const reqData = {
// company: {
// name: payload.payload.firmName,
// PIB: payload.payload.firmPIB,
// contacts: {
// telephone: payload.payload.firmPhone.toString(),
// location: payload.payload.firmLocation ?? "",
// web: payload.payload.firmWebsite,
// },
// },
// image: image,
// };

const reqData = {
company: {
name: payload.payload.firmName,
PIB: payload.payload.firmPIB,
contacts: {
telephone: payload.payload.firmPhone.toString(),
location: payload.payload.firmLocation ?? "",
web: payload.payload.firmWebsite,
},
},
image: image,
};
// if (payload.payload.firmLogo?.includes("https")) delete reqData.image;
// if (reqData.company.contacts.telephone.length === 0)
// delete reqData.company.contacts.telephone;
// if (reqData.company.contacts.location.length === 0)
// delete reqData.company.contacts.location;
// if (reqData.company.contacts.web.length === 0)
// delete reqData.company.contacts.web;

if (payload.payload.firmLogo.includes("https")) delete reqData.image;
if (reqData.company.contacts.telephone.length === 0)
delete reqData.company.contacts.telephone;
if (reqData.company.contacts.location.length === 0)
delete reqData.company.contacts.location;
if (reqData.company.contacts.web.length === 0)
delete reqData.company.contacts.web;
const requestBody = new FormData();
if (typeof payload.payload.firmLogo !== "string")
requestBody.append("file", payload.payload.firmLogo);
requestBody.append("company[name]", payload.payload.firmName);
requestBody.append("company[PIB]", payload.payload.firmPIB);
if (payload.payload.firmPhone.toString().length !== 0)
requestBody.append(
"company[contacts][telephone]",
payload.payload.firmPhone
);
if (payload.payload.firmLocation.toString().length !== 0)
requestBody.append(
"company[contacts][location]",
payload.payload.firmLocation
);
if (payload.payload.firmWebsite.toString().length !== 0)
requestBody.append("company[contacts][web]", payload.payload.firmWebsite);

const userId = yield select(selectUserId);
const data = yield call(attemptEditProfile, userId, reqData);
yield call(attemptEditProfile, userId, requestBody);
yield put(editMineProfileSuccess());
if (payload.payload.handleApiResponseSuccess) {
yield call(payload.payload.handleApiResponseSuccess);
}
console.log(data);
} catch (e) {
yield put(editMineProfileError());
console.dir(e);

+ 13
- 24
src/store/saga/registerSaga.js Bestand weergeven

@@ -21,30 +21,19 @@ import i18next from "i18next";

function* fetchRegisterUser({ payload }) {
try {
let requestData = {
email: payload.values.mail.toString(),
password: payload.values.password.toString(),
image: payload.values.image
.replace("data:image/jpeg;base64,", "")
.replace("data:image/jpg;base64,", "")
.replace("data:image/png;base64,", ""),
company: {
name: payload.values.nameOfFirm.toString(),
PIB: payload.values.PIB.toString(),
contacts: {
telephone: payload.values.phoneNumber.toString(),
location: payload.values.location.toString(),
web: payload.values.website.toString(),
},
},
};
if (payload.values.phoneNumber?.length === 0)
delete requestData.company.contacts.telephone;
if (payload.values.location?.length === 0)
delete requestData.company.contacts.location;
if (payload.values.website?.length === 0)
delete requestData.company.contacts.web;
yield call(attemptRegister, requestData);
const requestBody = new FormData();
requestBody.append("email", payload.values.mail);
requestBody.append("password", payload.values.password);
requestBody.append("file", payload.values.image);
requestBody.append("company[name]", payload.values.nameOfFirm);
requestBody.append("company[PIB]", payload.values.PIB);
if (payload.values.phoneNumber.toString().length !== 0)
requestBody.append("company[contacts][telephone]", payload.values.phoneNumber);
if (payload.values.location.toString().length !== 0)
requestBody.append("company[contacts][location]", payload.values.location);
if (payload.values.website.toString().length !== 0)
requestBody.append("company[contacts][web]", payload.values.website);
yield call(attemptRegister, requestBody);

const { data } = yield call(attemptLogin, {
email: payload.values.mail,

+ 1
- 3
src/util/helpers/chatHelper.js Bestand weergeven

@@ -1,10 +1,8 @@
import history from "../../store/utils/history";

export const startChat = (chats, offer, userId) => {
console.log(offer);
const chatItem = chats.find(
(item) =>
item.chat.offerId === offer?._id && offer?.userId !== userId
(item) => item.chat.offerId === offer?._id && offer?.userId !== userId
);
if (chatItem !== undefined) {
history.push(`/messages/${chatItem.chat._id}`);

+ 44
- 0
src/util/helpers/imageUrlGetter.js Bestand weergeven

@@ -0,0 +1,44 @@
const CLOUDFLARE = "CLOUDFLARE";
// const IMAGE_KIT = "IMAGEKIT";

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"
}

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",

}
export const getImageUrl = (imageUrl, variant, isMobile = false) => {
let imageVariant = "";
if (IMAGE_PLATFORM === CLOUDFLARE) {
imageVariant = isMobile ? cloudFlareVariants[variant + "Mobile"] : cloudFlareVariants[variant];
}
return imageUrl + imageVariant;
}

+ 1
- 4
src/validations/editProfileValidation.js Bestand weergeven

@@ -6,13 +6,10 @@ export default Yup.object().shape({
.required(i18n.t("editProfile.labelPIBRequired"))
.min(9, i18n.t("register.PIBnoOfCharacters"))
.max(9, i18n.t("register.PIBnoOfCharacters")),
firmLocation: Yup.string().required(
i18n.t("editProfile.labelLocationRequired")
),
firmLocation: Yup.string(),
firmWebsite: Yup.string(),
firmApplink: Yup.string(),
firmPhone: Yup.string()
.required(i18n.t("editProfile.labelPhoneRequired"))
.min(6, i18n.t("editProfile.labelPhoneValid"))
.max(14, i18n.t("editProfile.labelPhoneValid")),
});

Laden…
Annuleren
Opslaan