Преглед изворни кода

Saving informations on step back finished

bugfix/520
jovan.cirkovic пре 3 година
родитељ
комит
c90d155c45
100 измењених фајлова са 1723 додато и 2176 уклоњено
  1. 3
    0
      .eslintrc
  2. 15
    19
      .eslintrc.json
  3. 1
    0
      src/App.js
  4. 1
    1
      src/AppRoutes.js
  5. 8
    56
      src/components/Cards/ChatCard/ChatCard.js
  6. 6
    228
      src/components/Cards/ChatCard/ChatCard.styled.js
  7. 37
    0
      src/components/Cards/ChatCard/ChatCommands/ChatCommands.js
  8. 75
    0
      src/components/Cards/ChatCard/ChatCommands/ChatCommands.styled.js
  9. 36
    0
      src/components/Cards/ChatCard/LittleOfferDetails/LittleOfferDetails.js
  10. 85
    0
      src/components/Cards/ChatCard/LittleOfferDetails/LittleOfferDetails.styled.js
  11. 25
    0
      src/components/Cards/ChatCard/MobileOfferDetails/MobileOfferDetails.js
  12. 43
    0
      src/components/Cards/ChatCard/MobileOfferDetails/MobileOfferDetails.styled.js
  13. 21
    0
      src/components/Cards/ChatCard/OfferLocation/OfferLocation.js
  14. 33
    0
      src/components/Cards/ChatCard/OfferLocation/OfferLocation.styled.js
  15. 20
    4
      src/components/Cards/CreateOfferCard/CreateOffer.js
  16. 37
    22
      src/components/Cards/CreateOfferCard/FirstPart/FirstPartCreateOffer.js
  17. 30
    6
      src/components/Cards/CreateOfferCard/SecondPart/SecondPartCreateOffer.js
  18. 4
    1
      src/components/Cards/FilterCard/FilterCard.js
  19. 32
    0
      src/components/Cards/FilterCard/FilterDropdown/Checkbox/Checkbox/Checkbox.js
  20. 0
    0
      src/components/Cards/FilterCard/FilterDropdown/Checkbox/Checkbox/Checkbox.styled.js
  21. 84
    0
      src/components/Cards/FilterCard/FilterDropdown/Checkbox/CheckboxDropdownList/CheckboxDropdownList.js
  22. 25
    0
      src/components/Cards/FilterCard/FilterDropdown/Checkbox/CheckboxDropdownList/CheckboxDropdownList.styled.js
  23. 42
    0
      src/components/Cards/FilterCard/FilterDropdown/Checkbox/CheckboxDropdownList/SearchField/SearchField.js
  24. 17
    0
      src/components/Cards/FilterCard/FilterDropdown/Checkbox/CheckboxDropdownList/SearchField/SearchField.styled.js
  25. 29
    94
      src/components/Cards/FilterCard/FilterDropdown/Checkbox/FilterCheckboxDropdown.js
  26. 4
    37
      src/components/Cards/FilterCard/FilterDropdown/Checkbox/FilterCheckboxDropdown.styled.js
  27. 1
    1
      src/components/Cards/FilterCard/FilterDropdown/Radio/FilterRadioDropdown.js
  28. 1
    1
      src/components/Cards/FilterCard/FilterFooter/FilterFooter.js
  29. 8
    27
      src/components/Cards/ItemDetailsCard/ItemDetailsCard.js
  30. 1
    44
      src/components/Cards/ItemDetailsCard/ItemDetailsCard.styled.js
  31. 0
    36
      src/components/Cards/ItemDetailsCard/MockupOfferDetails.js
  32. 41
    9
      src/components/Cards/ItemDetailsCard/OfferDetails/OfferDetails.js
  33. 101
    0
      src/components/Cards/ItemDetailsCard/OfferDetails/OfferDetails.styled.js
  34. 6
    4
      src/components/Cards/MessageCard/MessageCard.js
  35. 16
    2
      src/components/Cards/OfferCard/OfferCard.js
  36. 7
    2
      src/components/Cards/OfferCard/OfferCard.styled.js
  37. 0
    24
      src/components/Cards/UserReviewsCard/Mockupdata.js
  38. 12
    36
      src/components/Cards/UserReviewsCard/UserReviewsCard.js
  39. 3
    3
      src/components/ChatColumn/ChatColumn.js
  40. 4
    14
      src/components/CreateReview/CreateReview.js
  41. 4
    2
      src/components/CreateReview/FirstStep/FirstStepCreateReview.js
  42. 13
    18
      src/components/DirectChat/DirectChat.js
  43. 3
    3
      src/components/DirectChat/DirectChatHeader/DirectChatHeader.js
  44. 16
    12
      src/components/DirectChat/DirectChatHeaderTitle/DirectChatHeaderTitle.js
  45. 9
    5
      src/components/DirectChat/DirectChatNewMessage/DirectChatNewMessage.js
  46. 18
    36
      src/components/DirectChat/MiniChatColumn/MiniChatColumn.js
  47. 18
    12
      src/components/DirectChat/MiniChatColumn/MiniChatColumnHeader/MiniChatColumnHeaderTitle.js
  48. 17
    14
      src/components/Dropdown/DropdownList/DropdownList.js
  49. 8
    3
      src/components/Header/Drawer/Drawer.js
  50. 0
    1
      src/components/Header/Header.js
  51. 12
    21
      src/components/ImagePicker/ImagePicker.js
  52. 0
    187
      src/components/InputFields/BaseInputField.js
  53. 0
    40
      src/components/InputFields/Checkbox.js
  54. 0
    123
      src/components/InputFields/CurrencyField.js
  55. 0
    33
      src/components/InputFields/EmailField.js
  56. 0
    74
      src/components/InputFields/NumberField.js
  57. 0
    74
      src/components/InputFields/PasswordField.js
  58. 0
    130
      src/components/InputFields/PasswordStrength.js
  59. 0
    45
      src/components/InputFields/PercentageField.js
  60. 0
    49
      src/components/InputFields/PhoneNumberField.js
  61. 0
    54
      src/components/InputFields/Radio.js
  62. 0
    37
      src/components/InputFields/Search.js
  63. 0
    122
      src/components/InputFields/SelectField.js
  64. 0
    72
      src/components/InputFields/TextField.js
  65. 2
    2
      src/components/ItemDetails/Header/Header.js
  66. 2
    1
      src/components/ItemDetails/ItemDetails.js
  67. 8
    60
      src/components/ItemDetails/ItemDetailsHeaderCard/ItemDetailsHeaderCard.js
  68. 2
    69
      src/components/ItemDetails/ItemDetailsHeaderCard/ItemDetailsHeaderCard.styled.js
  69. 30
    0
      src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/Category/CategoryDetail.js
  70. 37
    0
      src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/Category/CategoryDetail.styled.js
  71. 30
    0
      src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/PIB/PIBDetail.js
  72. 49
    0
      src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/PIB/PIBDetail.styled.js
  73. 60
    0
      src/components/ItemDetails/ItemDetailsHeaderCard/StatisticDetails/StatisticDetails.js
  74. 30
    0
      src/components/ItemDetails/ItemDetailsHeaderCard/StatisticDetails/StatisticDetails.styled.js
  75. 0
    59
      src/components/ItemDetails/MockupdataDetails.js
  76. 0
    26
      src/components/Loader/BlockSectionLoader.js
  77. 0
    13
      src/components/Loader/FullPageLoader.js
  78. 0
    10
      src/components/Loader/FullPageLoader.styled.js
  79. 0
    20
      src/components/Loader/SectionLoader.js
  80. 19
    0
      src/components/Login/Description/LoginDescription.js
  81. 18
    0
      src/components/Login/Description/LoginDescription.styled.js
  82. 26
    0
      src/components/Login/ErrorMessage/ErrorMessage.js
  83. 11
    0
      src/components/Login/ErrorMessage/ErrorMessage.styled.js
  84. 30
    0
      src/components/Login/Fields/Email/EmailField.js
  85. 0
    0
      src/components/Login/Fields/Email/EmailField.styled.js
  86. 50
    0
      src/components/Login/Fields/Password/PasswordField.js
  87. 0
    0
      src/components/Login/Fields/Password/PasswordField.styled.js
  88. 34
    0
      src/components/Login/ForgotPasswordLink/ForgotPasswordLink.js
  89. 0
    0
      src/components/Login/ForgotPasswordLink/ForgotPasswordLink.styled.js
  90. 98
    0
      src/components/Login/Login.js
  91. 19
    0
      src/components/Login/Login.styled.js
  92. 32
    0
      src/components/Login/LoginButton/LoginButton.js
  93. 0
    0
      src/components/Login/LoginButton/LoginButton.styled.js
  94. 27
    0
      src/components/Login/RegisterLink/RegisterLink.js
  95. 20
    0
      src/components/Login/RegisterLink/RegisterLink.styled.js
  96. 19
    0
      src/components/Login/Title/LoginTitle.js
  97. 17
    0
      src/components/Login/Title/LoginTitle.styled.js
  98. 0
    57
      src/components/MUI/DialogComponent.js
  99. 21
    21
      src/components/MUI/DrawerComponent.js
  100. 0
    0
      src/components/MUI/ErrorMessageComponent.js

+ 3
- 0
.eslintrc Прегледај датотеку

@@ -13,6 +13,9 @@
"react/jsx-filename-extension": "off",
"react/jsx-props-no-spreading": "off",
"react/button-has-type": "off",
"react/max-len": ["error", {
"code": 100
}],
"react/require-default-props": "off",
"import/no-extraneous-dependencies": "off",
"import/prefer-default-export": "off",

+ 15
- 19
.eslintrc.json Прегледај датотеку

@@ -1,22 +1,18 @@
{
"env": {
"browser": true,
"es2021": true
"env": {
"browser": true,
"es2021": true
},
"extends": ["eslint:recommended", "plugin:react/recommended"],
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
}
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": ["react"],
"rules": {
// "max-lines": ["warn", 100]
}
}

+ 1
- 0
src/App.js Прегледај датотеку

@@ -76,6 +76,7 @@ const App = () => {
<Router history={history}>
<Helmet>
<title>{i18next.t("app.title")}</title>
</Helmet>
<StyledEngineProvider injectFirst>
{/* <button onClick={handleClick}>Kik</button> */}

+ 1
- 1
src/AppRoutes.js Прегледај датотеку

@@ -40,7 +40,7 @@ import MyOffers from "./pages/MyOffers/MyOffers";
const AppRoutes = () => {
return (
<Switch>
<Route exact path={BASE_PAGE} component={LoginPage} />
<Route exact path={BASE_PAGE} component={HomePage} />
<Route exact path={LOGIN_PAGE} component={LoginPage} />
<Route path={NOT_FOUND_PAGE} component={NotFoundPage} />
<Route path={REGISTER_SUCCESSFUL_PAGE} component={RegisterSuccessful} />

+ 8
- 56
src/components/Cards/ChatCard/ChatCard.js Прегледај датотеку

@@ -1,41 +1,25 @@
import React, { useMemo } from "react";
import PropTypes from "prop-types";
import {
CheckButton,
OfferImage,
OfferTitle,
ChatOffer,
Commands,
ChatInfo,
OfferText,
ChatCardContainer,
Col,
UserImage,
OfferCardContainer,
UserImgWrapper,
OfferImgWrapper,
UserName,
LastMessage,
Line,
LocationContainer,
XSText,
LocationIcon,
OfferCardContainerMobile,
OfferTextMobile,
OfferTitleMobile,
PhoneIconContainer,
PhoneIcon,
LocationIconContainer,
} from "./ChatCard.styled";
import selectedTheme from "../../../themes";
import { useHistory } from "react-router-dom";
import useScreenDimensions from "../../../hooks/useScreenDimensions";
import { useTranslation } from "react-i18next";
import LittleOfferDetails from "./LittleOfferDetails/LittleOfferDetails";
import MobileOfferDetails from "./MobileOfferDetails/MobileOfferDetails";
import OfferLocation from "./OfferLocation/OfferLocation";
import ChatCommands from "./ChatCommands/ChatCommands";

const ChatCard = (props) => {
const history = useHistory();
const dimensions = useScreenDimensions();
const { t } = useTranslation();

const chat = useMemo(() => {
return props.chat;
@@ -47,7 +31,6 @@ const ChatCard = (props) => {
}
return "";
}, [chat]);

const routeToItem = () => {
history.push(`/messages/${chat?.chat?._id}`);
};
@@ -66,51 +49,20 @@ const ChatCard = (props) => {
<UserName>{chat?.interlocutorData?.name}</UserName>

{/* Only shows on Mobile */}
<OfferCardContainerMobile>
<OfferTextMobile>{t("messages.cardProduct")}</OfferTextMobile>
<OfferTitleMobile>{chat?.offerData?.name}</OfferTitleMobile>
</OfferCardContainerMobile>
<MobileOfferDetails chat={chat} />
{/* ^^^^^ */}

<LastMessage>{lastMessage}</LastMessage>
<LocationContainer>
<LocationIconContainer>
<LocationIcon />
</LocationIconContainer>
<XSText>{chat?.interlocutorData?.location}</XSText>
</LocationContainer>
<OfferLocation chat={chat} />
</ChatInfo>
</Col>
<Line />

{/* Only shows on Desktop */}
<Col mobileDisappear>
<ChatOffer>
<OfferImgWrapper>
<OfferImage src={chat?.offerData?.firstImage} />
</OfferImgWrapper>
<OfferCardContainer>
<OfferText>{t("messages.cardProduct")}</OfferText>
<OfferTitle>{chat?.offerData?.name}</OfferTitle>
</OfferCardContainer>
</ChatOffer>
</Col>
<LittleOfferDetails chat={chat} />
{/* ^^^^^^^ */}

<Commands>
<PhoneIconContainer>
<PhoneIcon />
</PhoneIconContainer>
<CheckButton
buttoncolor={selectedTheme.primaryPurple}
textcolor={selectedTheme.primaryPurple}
variant={"outlined"}
style={{ fontWeight: "600" }}
onClick={routeToItem}
>
{t("messages.seeChats")}
</CheckButton>
</Commands>
<ChatCommands routeToItem={() => routeToItem(chat?.chat?._id)} />
</ChatCardContainer>
);
};

+ 6
- 228
src/components/Cards/ChatCard/ChatCard.styled.js Прегледај датотеку

@@ -1,10 +1,6 @@
import { Box, Container, Typography } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../themes";
import { IconButton } from "../../Buttons/IconButton/IconButton";
import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton";
import { ReactComponent as Phone } from "../../../assets/images/svg/phone.svg";
import { ReactComponent as Location } from "../../../assets/images/svg/location.svg";

export const ChatCardContainer = styled(Container)`
display: flex;
@@ -14,13 +10,10 @@ export const ChatCardContainer = styled(Container)`
box-sizing: border-box;
margin: 10px 0;
background-color: ${(props) =>
props.sponsored === "true"
? selectedTheme.backgroundSponsoredColor
: "white"};
props.sponsored === "true" ? selectedTheme.backgroundSponsoredColor : "white"};
border-radius: 4px;
${(props) =>
props.sponsored === "true" &&
`border: 1px solid ${selectedTheme.borderSponsoredColor};`}
props.sponsored === "true" && `border: 1px solid ${selectedTheme.borderSponsoredColor};`}
padding: 16px;
max-width: 2000px;
height: 180px;
@@ -31,11 +24,9 @@ export const ChatCardContainer = styled(Container)`
margin: 0;
${(props) =>
props.vertical &&
`
height: 330px;
`height: 330px;
width: 180px;
margin: 0 18px;
`}
margin: 0 18px;`}
}
`;
export const UserImage = styled.img`
@@ -47,7 +38,6 @@ export const UserImage = styled.img`
height: 72px;
}
`;

export const UserImgWrapper = styled(Box)`
overflow: hidden;
border-radius: 50%;
@@ -59,191 +49,22 @@ export const UserImgWrapper = styled(Box)`
min-width: 80px;
}
`;
export const OfferImgWrapper = styled(Box)`
overflow: hidden;
border-radius: 4px;
width: 72px;
height: 72px;
min-width: 72px;
max-width: 72px;
`;
export const LocationIcon = styled(Location)`
height: 12px;
width: 12px;
`;

export const OfferCardContainer = styled(Container)`
display: flex;
flex-direction: column;
box-sizing: border-box;
padding: 16px;
max-width: 2000px;
position: relative;
@media (max-width: 550px) {
}
`;

export const OfferCardContainerMobile = styled(Box)`
display: none;

@media (max-width: 550px) {
position: relative;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
`;

export const OfferTitle = styled(Typography)`
font-family: "Open Sans";
flex: 1;
color: ${selectedTheme.primaryPurple};
font-weight: 700;
font-size: 18px;
cursor: pointer;
@media (max-width: 550px) {
font-size: 14px;
display: none;
${(props) =>
props.vertical &&
`
display: flex;
flex: none;
position: relative;
line-height: 22px;
margin-top: 5px;
font-size: 18px;

`}
}
`;
export const OfferTitleMobile = styled(Typography)`
font-family: "Open Sans";
display: none;
flex: 1;
color: ${selectedTheme.primaryPurple};
font-weight: 700;
font-size: 12px;
cursor: pointer;
@media (max-width: 550px) {
display: block;
${(props) =>
props.vertical &&
`
display: flex;
flex: none;
position: relative;
line-height: 22px;
margin-top: 5px;
font-size: 18px;

`}
}
`;

export const CheckButton = styled(PrimaryButton)`
width: 180px;
height: 48px;
&:hover {
background-color: ${selectedTheme.primaryPurple};
color: white !important;
border-radius: 4px;
}
@media (max-width: 1024px) {
width: 150px;
height: 40px;
margin-left: 2vw;
& button {
padding: 0;
font-size: 11px;
}
}
@media (max-width: 600px) {
display: none;
}
transition: 0.2s all;
`;
export const PhoneIconContainer = styled(IconButton)`
width: 40px;
height: 40px;
background-color: ${selectedTheme.primaryIconBackgroundColor};
border-radius: 100%;
padding-top: 2px;
text-align: center;
@media (max-width: 600px) {
width: 32px;
height: 32px;
top: 16px;
right: 16px;
padding: 0;
${(props) =>
props.vertical &&
`
display: none;
`}
& button svg {
width: 16px;
height: 16px;
position: relative;
top: -3px;
left: -2.4px;
}
}
`;

export const ChatOffer = styled(Box)`
display: flex;
flex-direction: row;
align-items: center;
padding-left: 36px;
@media (max-width: 600px) {
display: none;
}
`;

export const OfferText = styled(Box)`
font-family: "Open Sans";
font-size: "12px";
color: ${selectedTheme.primaryText};
`;

export const OfferTextMobile = styled(Box)`
font-family: "Open Sans";
font-size: 9px;
color: ${selectedTheme.primaryText};
`;

export const Commands = styled(Box)`
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: flex-end;
@media (max-width: 600px) {
align-items: flex-start;
}
`;

export const ChatInfo = styled(Box)`
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
`;

export const Col = styled(Box)`
display: flex;
align-items: center;
flex-direction: row;
gap: 18px;
flex: 1;
@media (max-width: 1024px) {
${(props) => props.mobileDisappear && `display: none;`}
}
@media (max-width: 600px) {
${(props) => props.mobileDisappear && `display: none;`}
${props => props.mobileDisappear && 'display: none;'}
}
`;

export const UserName = styled(Typography)`
margin-bottom: 12px;
font-family: "Open Sans";
@@ -255,7 +76,6 @@ export const UserName = styled(Typography)`
font-size: 18px;
}
`;

export const LastMessage = styled(Typography)`
font-family: "Open Sans";
color: ${selectedTheme.primaryDarkTextThird};
@@ -273,39 +93,6 @@ export const LastMessage = styled(Typography)`
display: none;
}
`;

export const LocationContainer = styled(Box)`
display: flex;
flex-direction: row;
gap: 2px;
@media (max-width: 600px) {
display: none;
}
`;

export const LocationIconContainer = styled(Box)`
height: 12px;
width: auto;
position: relative;
`;

export const XSText = styled(Typography)`
font-family: "Open Sans";
color: ${selectedTheme.primaryText};
line-height: 16px;
font-size: 14px;
position: relative;
`;

export const OfferImage = styled.img`
max-width: 72px;
max-height: 72px;
min-width: 72px;
width: 72px;
height: 72px;
border-radius: 4px;
`;

export const Line = styled(Box)`
border-left: 1px solid rgba(0, 0, 0, 0.15);
height: 100px;
@@ -313,13 +100,4 @@ export const Line = styled(Box)`
margin: auto 0;
@media (max-width: 600px) {
display: none;
}
`;
export const PhoneIcon = styled(Phone)`
@media (max-width: 600px) {
width: 14px;
height: 14px;
position: relative;
top: 1px;
}
`;
}`;

+ 37
- 0
src/components/Cards/ChatCard/ChatCommands/ChatCommands.js Прегледај датотеку

@@ -0,0 +1,37 @@
import React from "react";
import PropTypes from "prop-types";
import {
CheckButton,
Commands,
PhoneIcon,
PhoneIconContainer,
} from "./ChatCommands.styled";
import selectedTheme from "../../../../themes";
import { useTranslation } from "react-i18next";

const ChatCommands = (props) => {
const { t } = useTranslation();

return (
<Commands>
<PhoneIconContainer>
<PhoneIcon />
</PhoneIconContainer>
<CheckButton
buttoncolor={selectedTheme.primaryPurple}
textcolor={selectedTheme.primaryPurple}
variant={"outlined"}
style={{ fontWeight: "600" }}
onClick={props.routeToItem}
>
{t("messages.seeChats")}
</CheckButton>
</Commands>
);
};

ChatCommands.propTypes = {
routeToItem: PropTypes.func,
};

export default ChatCommands;

+ 75
- 0
src/components/Cards/ChatCard/ChatCommands/ChatCommands.styled.js Прегледај датотеку

@@ -0,0 +1,75 @@
import { Box } from "@mui/material";
import styled from "styled-components";
import { ReactComponent as Phone } from "../../../../assets/images/svg/phone.svg";
import selectedTheme from "../../../../themes";
import { PrimaryButton } from "../../../Buttons/PrimaryButton/PrimaryButton";
import IconButton from "../../../IconButton/IconButton";


export const Commands = styled(Box)`
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: flex-end;
@media (max-width: 600px) {
align-items: flex-start;
}
`;
export const PhoneIcon = styled(Phone)`
@media (max-width: 600px) {
width: 14px;
height: 14px;
position: relative;
top: 1px;
}
`;
export const PhoneIconContainer = styled(IconButton)`
width: 40px;
height: 40px;
background-color: ${selectedTheme.primaryIconBackgroundColor};
border-radius: 100%;
padding-top: 2px;
text-align: center;
@media (max-width: 600px) {
width: 32px;
height: 32px;
top: 16px;
right: 16px;
padding: 0;
${(props) =>
props.vertical &&
`
display: none;
`}
& button svg {
width: 16px;
height: 16px;
position: relative;
top: -3px;
left: -2.4px;
}
}
`;

export const CheckButton = styled(PrimaryButton)`
width: 180px;
height: 48px;
&:hover {
background-color: ${selectedTheme.primaryPurple};
color: white !important;
border-radius: 4px;
}
@media (max-width: 1024px) {
width: 150px;
height: 40px;
margin-left: 2vw;
& button {
padding: 0;
font-size: 11px;
}
}
@media (max-width: 600px) {
display: none;
}
transition: 0.2s all;
`;

+ 36
- 0
src/components/Cards/ChatCard/LittleOfferDetails/LittleOfferDetails.js Прегледај датотеку

@@ -0,0 +1,36 @@
import React from "react";
import PropTypes from "prop-types";
import {
ChatOffer,
OfferCardContainer,
OfferImage,
OfferImgWrapper,
OfferText,
OfferTitle,
} from "./LittleOfferDetails.styled";
import { useTranslation } from "react-i18next";
import { Col } from "../ChatCard.styled";

const LittleOfferDetails = (props) => {
const chat = props.chat;
const { t } = useTranslation();
return (
<Col mobileDisappear>
<ChatOffer>
<OfferImgWrapper>
<OfferImage src={chat?.offerData?.firstImage} />
</OfferImgWrapper>
<OfferCardContainer>
<OfferText>{t("messages.cardProduct")}</OfferText>
<OfferTitle>{chat?.offerData?.name}</OfferTitle>
</OfferCardContainer>
</ChatOffer>
</Col>
);
};

LittleOfferDetails.propTypes = {
chat: PropTypes.any,
};

export default LittleOfferDetails;

+ 85
- 0
src/components/Cards/ChatCard/LittleOfferDetails/LittleOfferDetails.styled.js Прегледај датотеку

@@ -0,0 +1,85 @@
import { Box, Container, Typography } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../../themes";

export const ChatOffer = styled(Box)`
display: flex;
flex-direction: row;
align-items: center;
padding-left: 36px;
@media (max-width: 600px) {
display: none;
}
`;

export const OfferText = styled(Box)`
font-family: "Open Sans";
font-size: "12px";
color: ${selectedTheme.primaryText};
`;

export const OfferTitle = styled(Typography)`
font-family: "Open Sans";
flex: 1;
color: ${selectedTheme.primaryPurple};
font-weight: 700;
font-size: 18px;
cursor: pointer;
@media (max-width: 550px) {
font-size: 14px;
display: none;
${(props) =>
props.vertical &&
`
display: flex;
flex: none;
position: relative;
line-height: 22px;
margin-top: 5px;
font-size: 18px;

`}
}
`;

export const OfferCardContainer = styled(Container)`
display: flex;
flex-direction: column;
box-sizing: border-box;
padding: 16px;
max-width: 2000px;
position: relative;
@media (max-width: 550px) {
}
`;

export const OfferImgWrapper = styled(Box)`
overflow: hidden;
border-radius: 4px;
width: 72px;
height: 72px;
min-width: 72px;
max-width: 72px;
`;

export const OfferImage = styled.img`
max-width: 72px;
max-height: 72px;
min-width: 72px;
width: 72px;
height: 72px;
border-radius: 4px;
`;
export const Col = styled(Box)`
display: flex;
align-items: center;
flex-direction: row;
gap: 18px;
flex: 1;
@media (max-width: 1024px) {
${(props) => props.mobileDisappear && `display: none;`}
}
@media (max-width: 600px) {
${(props) => props.mobileDisappear && `display: none;`}
}
`;

+ 25
- 0
src/components/Cards/ChatCard/MobileOfferDetails/MobileOfferDetails.js Прегледај датотеку

@@ -0,0 +1,25 @@
import React from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
import {
OfferCardContainerMobile,
OfferTextMobile,
OfferTitleMobile,
} from "./MobileOfferDetails.styled";

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

MobileOfferDetails.propTypes = {
chat: PropTypes.any,
};

export default MobileOfferDetails;

+ 43
- 0
src/components/Cards/ChatCard/MobileOfferDetails/MobileOfferDetails.styled.js Прегледај датотеку

@@ -0,0 +1,43 @@
import { Box, Typography } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../../themes";

export const OfferCardContainerMobile = styled(Box)`
display: none;

@media (max-width: 550px) {
position: relative;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
`;

export const OfferTitleMobile = styled(Typography)`
font-family: "Open Sans";
display: none;
flex: 1;
color: ${selectedTheme.primaryPurple};
font-weight: 700;
font-size: 12px;
cursor: pointer;
@media (max-width: 550px) {
display: block;
${(props) =>
props.vertical &&
`
display: flex;
flex: none;
position: relative;
line-height: 22px;
margin-top: 5px;
font-size: 18px;

`}
}
`;
export const OfferTextMobile = styled(Box)`
font-family: "Open Sans";
font-size: 9px;
color: ${selectedTheme.primaryText};
`;

+ 21
- 0
src/components/Cards/ChatCard/OfferLocation/OfferLocation.js Прегледај датотеку

@@ -0,0 +1,21 @@
import React from 'react'
import PropTypes from 'prop-types'
import { LocationContainer, LocationIcon, LocationIconContainer, XSText } from './OfferLocation.styled';

const OfferLocation = (props) => {
const chat = props.chat;
return (
<LocationContainer>
<LocationIconContainer>
<LocationIcon />
</LocationIconContainer>
<XSText>{chat?.interlocutorData?.location}</XSText>
</LocationContainer>
)
}

OfferLocation.propTypes = {
chat: PropTypes.any,
}

export default OfferLocation

+ 33
- 0
src/components/Cards/ChatCard/OfferLocation/OfferLocation.styled.js Прегледај датотеку

@@ -0,0 +1,33 @@
import { Box, Typography } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../../themes";
import { ReactComponent as Location } from "../../../../assets/images/svg/location.svg";


export const LocationContainer = styled(Box)`
display: flex;
flex-direction: row;
gap: 2px;
@media (max-width: 600px) {
display: none;
}
`;

export const LocationIconContainer = styled(Box)`
height: 12px;
width: auto;
position: relative;
`;

export const XSText = styled(Typography)`
font-family: "Open Sans";
color: ${selectedTheme.primaryText};
line-height: 16px;
font-size: 14px;
position: relative;
`;

export const LocationIcon = styled(Location)`
height: 12px;
width: 12px;
`;

+ 20
- 4
src/components/Cards/CreateOfferCard/CreateOffer.js Прегледај датотеку

@@ -123,19 +123,27 @@ const CreateOffer = ({ history, closeCreateOfferModal, editOffer, offer }) => {
category,
condition,
description,
// images,
images,
location,
nameOfProduct,
subcategory,
} = informations;
if (stepNumber === 1) {
setInformations({});
setInformations({
category,
condition,
description,
location,
nameOfProduct,
subcategory,
});
}
if (stepNumber === 2) {
setInformations({
category,
condition,
description,
images,
location,
nameOfProduct,
subcategory,
@@ -177,10 +185,18 @@ const CreateOffer = ({ history, closeCreateOfferModal, editOffer, offer }) => {
functions={[() => goStepBack(1), () => goStepBack(2)]}
/>
{currentStep === 1 && (
<FirstPartCreateOffer handleNext={handleNext} offer={offer} />
<FirstPartCreateOffer
handleNext={handleNext}
offer={offer}
informations={informations}
/>
)}
{currentStep === 2 && (
<SecondPartCreateOffer handleNext={handleNext} offer={offer} />
<SecondPartCreateOffer
handleNext={handleNext}
offer={offer}
informations={informations}
/>
)}
{currentStep === 3 && (
<ThirdPartCreateOffer

+ 37
- 22
src/components/Cards/CreateOfferCard/FirstPart/FirstPartCreateOffer.js Прегледај датотеку

@@ -24,12 +24,31 @@ const FirstPartCreateOffer = (props) => {

const { t } = useTranslation();

useEffect(() => {
if (!props.offer) {
if (Object.keys(props.informations).length !== 0) {
formik.setFieldValue("nameOfProduct", props.informations.nameOfProduct);
formik.setFieldValue("description", props.informations.description);
formik.setFieldValue("location", props.informations.location);
formik.setFieldValue("category", props.informations.category);
formik.setFieldValue("subcategory", props.informations.subcategory);
let scat = categories.filter(
(cat) => cat.name === props.informations.category
);
setSubcat(scat[0].subcategories.map((x) => x.name));
}
} else {
formik.setFieldValue("location", props.offer.location.city);
formik.setFieldValue("category", props.offer.category.name);
formik.setFieldValue("subcategory", props.offer.subcategory);
}
}, [props.offer, props.informations]);

useEffect(() => {
if (props.offer !== undefined) {
let scat = categories.filter(
(cat) => cat.name === props.offer.category.name
);
console.log(scat[0].subcategories.map((x) => x.name));
setSubcat(scat[0].subcategories.map((x) => x.name));
}
}, [props.offer]);
@@ -39,15 +58,11 @@ const FirstPartCreateOffer = (props) => {
};
const formik = useFormik({
initialValues: {
nameOfProduct: `${props.offer === undefined ? "" : props.offer.name}`,
description: `${
props.offer === undefined ? "" : props.offer.description
}`,
location: `${props.offer === undefined ? "" : props.offer.location.city}`,
category: `${props.offer === undefined ? "" : props.offer.category.name}`,
subcategory: `${
props.offer === undefined ? "" : props.offer.subcategory
}`,
nameOfProduct: `${!props.offer ? "" : props.offer.name}`,
description: `${!props.offer ? "" : props.offer.description}`,
location: "default",
category: "default",
subcategory: "default",
},
validationSchema: Yup.object().shape({
nameOfProduct: Yup.string().required(t("login.nameOfProductRequired")),
@@ -119,19 +134,18 @@ const FirstPartCreateOffer = (props) => {

<FieldLabel leftText={t("offer.location")} />
<SelectField
defaultValue={
props.offer === undefined ? "default" : props.offer.location.city
}
defaultValue={formik.values.location}
onChange={(value) => {
formik.setFieldValue("location", value.target.value);
}}
value={formik.values.location}
>
<SelectOption value="default">
{t("offer.choseLocation")}
</SelectOption>
{locations.map((loc) => {
return (
<SelectOption key={loc._if} value={loc.city}>
<SelectOption key={loc._id} value={loc.city}>
{loc.city}
</SelectOption>
);
@@ -140,12 +154,11 @@ const FirstPartCreateOffer = (props) => {

<FieldLabel leftText={t("offer.category")} />
<SelectField
defaultValue={
props.offer === undefined ? "default" : props.offer.category.name
}
defaultValue={formik.values.category}
onChange={(value) => {
formik.setFieldValue("category", value.target.value);
}}
value={formik.values.category}
>
<SelectOption value="default">
{t("offer.choseCategory")}
@@ -165,13 +178,11 @@ const FirstPartCreateOffer = (props) => {

<FieldLabel leftText={t("offer.subcategory")} />
<SelectField
defaultValue={
props.offer === undefined ? "default" : props.offer.subcategory
}
// defaultValue="default"
defaultValue={formik.values.subcategory}
onChange={(value) => {
formik.setFieldValue("subcategory", value.target.value);
}}
value={formik.values.subcategory}
>
<SelectOption value="default">
{t("offer.choseSubcategory")}
@@ -202,10 +213,13 @@ const FirstPartCreateOffer = (props) => {
!formik.values?.description ||
formik.values?.category?.length === 0 ||
!formik.values?.category ||
formik.values?.category === "default" ||
formik.values?.subcategory?.length === 0 ||
!formik.values?.subcategory ||
formik.values?.subcategory === "default" ||
formik.values?.location?.length === 0 ||
!formik.values?.location
!formik.values?.location ||
formik.values?.location === "default"
}
>
{t("offer.continue")}
@@ -218,6 +232,7 @@ FirstPartCreateOffer.propTypes = {
children: PropTypes.any,
handleNext: PropTypes.func,
offer: PropTypes.node,
informations: PropTypes.any,
};

export default FirstPartCreateOffer;

+ 30
- 6
src/components/Cards/CreateOfferCard/SecondPart/SecondPartCreateOffer.js Прегледај датотеку

@@ -27,6 +27,17 @@ const SecondPartCreateOffer = (props) => {
); // 3 images
const { t } = useTranslation();

useEffect(() => {
if (!props.offer) {
if (Object.keys(props.informations).length > 5) {
setImages([...props.informations.images]);
formik.setFieldValue("condition", props.informations.condition);
}
} else {
formik.setFieldValue("condition", props.offer.condition);
}
}, [props.offer, props.informations]);

useEffect(() => {
setImages((prevState) => {
let editedImages = [...prevState];
@@ -63,12 +74,21 @@ const SecondPartCreateOffer = (props) => {
props.handleNext(values);
};

const conditionSelectEnumArray = Object.values(conditionSelectEnum);
const filteredconditionSelectEnumArray = conditionSelectEnumArray.map(
(item) => item.mainText
);

const formik = useFormik({
initialValues: {
images: images,
condition: `${props.offer === undefined ? "" : props.offer.condition}`,
condition: props.informations?.condition || "default",
},
validationSchema: Yup.object().shape({}),
validationSchema: Yup.object().shape({
condition: Yup.string()
.required()
.oneOf(filteredconditionSelectEnumArray),
}),
onSubmit: handleSubmit,
validateOnBlur: true,
enableReinitialize: true,
@@ -96,12 +116,10 @@ const SecondPartCreateOffer = (props) => {
<InputButtonContainer>
<FieldLabel leftText={t("offer.condition")} />
<SelectField
defaultValue={
props.offer === undefined ? "default" : props.offer.condition
}
onChange={(value) => {
formik.setFieldValue("condition", value.target.value);
}}
value={formik.values.condition}
>
<SelectOption value="default">
{t("offer.choseCondition")}
@@ -127,7 +145,12 @@ const SecondPartCreateOffer = (props) => {
textcolor="white"
onClick={formik.handleSubmit}
disabled={
props.offer === undefined ? imagesEmpty === numberOfImages : false
(props.offer === undefined
? imagesEmpty === numberOfImages
: false) ||
formik.values?.condition?.length === 0 ||
!formik.values?.condition ||
formik.values?.condition === "default"
}
>
{t("offer.continue")}
@@ -140,6 +163,7 @@ SecondPartCreateOffer.propTypes = {
children: PropTypes.node,
handleNext: PropTypes.func,
offer: PropTypes.node,
informations: PropTypes.any,
};

export default SecondPartCreateOffer;

+ 4
- 1
src/components/Cards/FilterCard/FilterCard.js Прегледај датотеку

@@ -33,7 +33,10 @@ const FilterCard = (props) => {
<LocationChoser filters={filters} />
</ContentContainer>

<FilterFooter />
<FilterFooter
closeResponsive={props.closeResponsive}
responsiveOpen={props.responsiveOpen}
/>
</FilterCardContainer>
);
};

+ 32
- 0
src/components/Cards/FilterCard/FilterDropdown/Checkbox/Checkbox/Checkbox.js Прегледај датотеку

@@ -0,0 +1,32 @@
import React from "react";
import PropTypes from "prop-types";
import { CheckBox as CheckboxButton } from "../../../../../CheckBox/CheckBox";

const Checkbox = (props) => {
const item = props.item;
return (
<CheckboxButton
leftText={item.city}
rightText={item.offerCount}
value={item}
checked={
props.filters.find(
(itemInList) =>
itemInList?.city?.toString() === item?.city?.toString()
)
? true
: false
}
onChange={props.onChange}
fullWidth
/>
);
};

Checkbox.propTypes = {
item: PropTypes.any,
filters: PropTypes.any,
onChange: PropTypes.func,
};

export default Checkbox;

+ 0
- 0
src/components/Cards/FilterCard/FilterDropdown/Checkbox/Checkbox/Checkbox.styled.js Прегледај датотеку


+ 84
- 0
src/components/Cards/FilterCard/FilterDropdown/Checkbox/CheckboxDropdownList/CheckboxDropdownList.js Прегледај датотеку

@@ -0,0 +1,84 @@
import React from "react";
import PropTypes from "prop-types";
import DropdownList from "../../../../../Dropdown/DropdownList/DropdownList";
import selectedTheme from "../../../../../../themes";
import IconWithNumber from "../../../../../Icon/IconWithNumber/IconWithNumber";
import { ReactComponent as DropdownDown } from "../../../../../../assets/images/svg/down-arrow.svg";
import { ReactComponent as DropdownUp } from "../../../../../../assets/images/svg/up-arrow.svg";
import { ReactComponent as Close } from "../../../../../../assets/images/svg/close-white.svg";
import {
SelectedItem,
SelectedItemsContainer,
} from "./CheckboxDropdownList.styled";
import SearchField from "./SearchField/SearchField";

const CheckboxDropdownList = (props) => {
const data = props.data;
const handleDelete = (item) => {
props.setItemsSelected([...props.filters.filter((p) => p !== item)]);
};
return (
<DropdownList
title={props.title}
textcolor={
props.filters.length > 0
? selectedTheme.primaryPurple
: selectedTheme.primaryText
}
dropdownIcon={
<IconWithNumber number={props.filters.length}>
{props.icon}
</IconWithNumber>
}
toggleIconClosed={<DropdownDown />}
toggleIconOpened={<DropdownUp />}
fullWidth
open={props.isOpened}
setIsOpened={props.setIsOpened}
toggleIconStyles={{
backgroundColor: props.isOpened
? "white"
: selectedTheme.primaryIconBackgroundColor,
}}
headerOptions={
<React.Fragment>
<SelectedItemsContainer>
{props.filters.map((item) => (
<SelectedItem key={item.city} onClick={() => handleDelete(item)}>
{
data.find(
(p) => p?.city?.toString() === item?.city?.toString()
)?.city
}
<Close style={{ position: "relative", top: "3px" }} />
</SelectedItem>
))}
</SelectedItemsContainer>
<SearchField
placeholder={props.searchPlaceholder}
value={props.toSearch}
onChange={(event) => props.setToSearch(event.target.value)}
/>
</React.Fragment>
}
>
{props.children}
</DropdownList>
);
};

CheckboxDropdownList.propTypes = {
children: PropTypes.node,
title: PropTypes.string,
filters: PropTypes.any,
icon: PropTypes.node,
setToSearch: PropTypes.func,
setItemsSelected: PropTypes.func,
data: PropTypes.any,
searchPlaceholder: PropTypes.string,
toSearch: PropTypes.string,
isOpened: PropTypes.bool,
setIsOpened: PropTypes.func,
};

export default CheckboxDropdownList;

+ 25
- 0
src/components/Cards/FilterCard/FilterDropdown/Checkbox/CheckboxDropdownList/CheckboxDropdownList.styled.js Прегледај датотеку

@@ -0,0 +1,25 @@
import { Box } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../../../../themes";

export const SelectedItemsContainer = styled(Box)`
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin-top: 5px;
`;
export const SelectedItem = styled(Box)`
margin-top: 2px;
background-color: ${selectedTheme.primaryPurple};
border-radius: 8px;
color: white;
padding-left: 8px;
padding-right: 6px;
line-height: 12px;
letter-spacing: 0.02em;
font-family: "Open Sans";
font-size: 12px;
cursor: pointer;
margin-right: 3px;
height: 22px;
`;

+ 42
- 0
src/components/Cards/FilterCard/FilterDropdown/Checkbox/CheckboxDropdownList/SearchField/SearchField.js Прегледај датотеку

@@ -0,0 +1,42 @@
import React from "react";
import PropTypes from "prop-types";
import { TextField } from "../../../../../../TextFields/TextField/TextField";
import { ReactComponent as CloseBlack } from "../../../../../../../assets/images/svg/close-black.svg";
import { ClearText } from "./SearchField.styled";

const SearchField = (props) => {
const handleClear = () => {
props.onChange("");
};
return (
<TextField
placeholder={props.placeholder}
italicPlaceholder
value={props.value}
onChange={props.onChange}
textsize={"12px"}
font={"Open Sans"}
fullWidth
height={"40px"}
containerStyle={{ marginTop: "6px" }}
InputProps={{
endAdornment:
props.value.length > 0 ? (
<ClearText onClick={handleClear}>
<CloseBlack />
</ClearText>
) : (
<React.Fragment />
),
}}
/>
);
};

SearchField.propTypes = {
value: PropTypes.string,
placeholder: PropTypes.string,
onChange: PropTypes.func,
};

export default SearchField;

+ 17
- 0
src/components/Cards/FilterCard/FilterDropdown/Checkbox/CheckboxDropdownList/SearchField/SearchField.styled.js Прегледај датотеку

@@ -0,0 +1,17 @@
import { Box } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../../../../../themes";

export const ClearText = styled(Box)`
padding-top: 1px;
border-radius: 100%;
cursor: pointer;
padding-right: 2px;
position: relative;
left: 6px;
width: 21px;
height: 21px;
&:hover {
background-color: ${selectedTheme.primaryIconBackgroundColor};
}
`;

+ 29
- 94
src/components/Cards/FilterCard/FilterDropdown/Checkbox/FilterCheckboxDropdown.js Прегледај датотеку

@@ -1,30 +1,19 @@
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import DropdownList from "../../../../Dropdown/DropdownList/DropdownList";
import selectedTheme from "../../../../../themes";
import IconWithNumber from "../../../../Icon/IconWithNumber/IconWithNumber";
import { ReactComponent as DropdownDown } from "../../../../../assets/images/svg/down-arrow.svg";
import { ReactComponent as DropdownUp } from "../../../../../assets/images/svg/up-arrow.svg";
import { ReactComponent as Close } from "../../../../../assets/images/svg/close-white.svg";
import { ReactComponent as CloseBlack } from "../../../../../assets/images/svg/close-black.svg";
import {
ClearText,
SelectedItem,
SelectedItemsContainer,
} from "./FilterCheckboxDropdown.styled";
import { TextField } from "../../../../TextFields/TextField/TextField";
import DropdownItem from "../../../../Dropdown/DropdownItem/DropdownItem";
import { CheckBox } from "../../../../CheckBox/CheckBox";
import CheckboxDropdownList from "./CheckboxDropdownList/CheckboxDropdownList";
import Checkbox from "./Checkbox/Checkbox";

const FilterCheckboxDropdown = (props) => {
const [toSearch, setToSearch] = useState("");
const [dataToShow, setDataToShow] = useState([]);
const [isOpened, setIsOpened] = useState(false);
const [toSearch, setToSearch] = useState("");
const { data } = props;

useEffect(() => {
setDataToShow([...data]);
}, [data]);

useEffect(() => {
if (toSearch.length > 0) {
setDataToShow(
@@ -37,110 +26,57 @@ const FilterCheckboxDropdown = (props) => {
}
}, [toSearch]);


useEffect(() => {
if (props.filters?.length > 0) {
setIsOpened(true)
setIsOpened(true);
}
}, [props.filters])
}, [props.filters]);

const handleChange = (item) => {
if (props.oneValueAllowed) {
props.setItemsSelected([item]);
} else {
if (props.filters.find(itemInList => itemInList?.city?.toString() === item?.city?.toString())) {
props.setItemsSelected([...props.filters.filter((p) => p?.city?.toString() !== item?.city?.toString())]);
if (
props.filters.find(
(itemInList) =>
itemInList?.city?.toString() === item?.city?.toString()
)
) {
props.setItemsSelected([
...props.filters.filter(
(p) => p?.city?.toString() !== item?.city?.toString()
),
]);
} else {
props.setItemsSelected([...props.filters, item]);
}
}
};
const handleDelete = (item) => {
props.setItemsSelected([...props.filters.filter((p) => p !== item)]);
};
const handleClear = () => {
setToSearch("");
};

return (
<DropdownList
<CheckboxDropdownList
toSearch={toSearch}
setToSearch={setToSearch}
title={props.title}
textcolor={
props.filters.length > 0
? selectedTheme.primaryPurple
: selectedTheme.primaryText
}
dropdownIcon={
<IconWithNumber number={props.filters.length}>
{props.icon}
</IconWithNumber>
}
toggleIconClosed={<DropdownDown />}
toggleIconOpened={<DropdownUp />}
fullWidth
open={isOpened}
filters={props.filters}
icon={props.icon}
data={data}
searchPlaceholder={props.searchPlaceholder}
isOpened={isOpened}
setIsOpened={setIsOpened}
toggleIconStyles={{
backgroundColor: isOpened
? "white"
: selectedTheme.primaryIconBackgroundColor,
}}
headerOptions={
<React.Fragment>
<SelectedItemsContainer>
{props.filters.map((item) => (
<SelectedItem key={item.city} onClick={() => handleDelete(item)}>
{
data.find((p) => p?.city?.toString() === item?.city?.toString())
?.city
}
<Close style={{ position: "relative", top: "3px" }} />
</SelectedItem>
))}
</SelectedItemsContainer>

<TextField
placeholder={props.searchPlaceholder}
italicPlaceholder
value={toSearch}
onChange={(event) => setToSearch(event.target.value)}
textsize={"12px"}
font={"Open Sans"}
fullWidth
height={"40px"}
containerStyle={{ marginTop: "6px" }}
InputProps={{
endAdornment:
toSearch.length > 0 ? (
<ClearText onClick={handleClear}>
<CloseBlack />
</ClearText>
) : (
<React.Fragment />
),
}}
/>
</React.Fragment>
}
>
{dataToShow.map((item) => {
return (
<DropdownItem key={item.city}>
<CheckBox
leftText={item.city}
rightText={item.offerCount}
value={item}
checked={props.filters.find(
(itemInList) =>
itemInList?.city?.toString() === item?.city?.toString()
) ? true : false}
<Checkbox
item={item}
filters={props.filters}
onChange={() => handleChange(item)}
fullWidth
/>
</DropdownItem>
);
})}
</DropdownList>
</CheckboxDropdownList>
);
};

@@ -157,5 +93,4 @@ FilterCheckboxDropdown.propTypes = {
FilterCheckboxDropdown.defaultProps = {
oneValueAllowed: false,
};

export default FilterCheckboxDropdown;

+ 4
- 37
src/components/Cards/FilterCard/FilterDropdown/Checkbox/FilterCheckboxDropdown.styled.js Прегледај датотеку

@@ -1,38 +1,5 @@
import { Box } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../../../themes";
// import { Box } from "@mui/material";
// import styled from "styled-components";
// import selectedTheme from "../../../../../themes";


export const SelectedItemsContainer = styled(Box)`
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin-top: 5px;
`;
export const SelectedItem = styled(Box)`
margin-top: 2px;
background-color: ${selectedTheme.primaryPurple};
border-radius: 8px;
color: white;
padding-left: 8px;
padding-right: 6px;
line-height: 12px;
letter-spacing: 0.02em;
font-family: "Open Sans";
font-size: 12px;
cursor: pointer;
margin-right: 3px;
height: 22px;
`;
export const ClearText = styled(Box)`
padding-top: 1px;
border-radius: 100%;
cursor: pointer;
padding-right: 2px;
position: relative;
left: 6px;
width: 21px;
height: 21px;
&:hover {
background-color: ${selectedTheme.primaryIconBackgroundColor};
}
`;

+ 1
- 1
src/components/Cards/FilterCard/FilterDropdown/Radio/FilterRadioDropdown.js Прегледај датотеку

@@ -124,7 +124,7 @@ const FilterRadioDropdown = (props) => {
>
<RadioButton
value={item}
label={item.name ? item.name : item}
label={item?.name ? item?.name : item?.length > 0 ? item : ""}
number={item.offerCount}
fullWidth
checked={

+ 1
- 1
src/components/Cards/FilterCard/FilterFooter/FilterFooter.js Прегледај датотеку

@@ -14,7 +14,7 @@ const FilterFooter = (props) => {
if (props.closeResponsive) props.closeResponsive();
};
return (
<FilterFooterContainer>
<FilterFooterContainer responsiveOpen={props.responsiveOpen}>
{props.responsiveOpen && (
<PrimaryButton
variant="outlined"

+ 8
- 27
src/components/Cards/ItemDetailsCard/ItemDetailsCard.js Прегледај датотеку

@@ -7,13 +7,6 @@ import {
Info,
ButtonsContainer,
PostDate,
OfferTitle,
OfferDescriptionText,
OfferDescriptionTitle,
Details,
OfferDetails,
OfferImage,
Scroller,
CategoryIcon,
SubcategoryIcon,
QuantityIcon,
@@ -36,6 +29,7 @@ import Information from "./Information/Information";
import { useTranslation } from "react-i18next";
import DeleteOffer from "../OfferCard/DeleteOffer/DeleteOffer";
import CreateOffer from "../CreateOfferCard/CreateOffer";
import OfferDetails from "./OfferDetails/OfferDetails";

const ItemDetailsCard = (props) => {
const [showModalRemove, setShowModalRemove] = useState(false);
@@ -67,7 +61,7 @@ const ItemDetailsCard = (props) => {
const date = formatDateLocale(new Date(offer?.offer?._created));

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

const closeEditModalHandler = () => {
@@ -120,25 +114,12 @@ const ItemDetailsCard = (props) => {
)}
</PostDate>
</OfferInfo>
<Details
hasScrollBar={!props.showPublishButton}
exchange={props.showExchangeButton}
>
<OfferTitle>{offer?.offer?.name}</OfferTitle>
<Scroller>
{offer?.offer?.images?.map((item) => {
return <OfferImage src={item} key={item} />;
})}
</Scroller>
<OfferDetails>
<OfferDescriptionTitle>
{t("itemDetailsCard.description")}
</OfferDescriptionTitle>
<OfferDescriptionText showBarterButton={props.showExchangeButton}>
{offer?.offer?.description}
</OfferDescriptionText>
</OfferDetails>
</Details>
<OfferDetails
offer={offer}
showExchangeButton={props.showExchangeButton}
showPublishButton={props.showPublishButton}
/>

{!props.halfwidth && props.showExchangeButton && (
<CheckButton
variant={props.sponsored ? "contained" : "outlined"}

+ 1
- 44
src/components/Cards/ItemDetailsCard/ItemDetailsCard.styled.js Прегледај датотеку

@@ -4,7 +4,6 @@ import selectedTheme from "../../../themes";
//import { IconButton } from "../../Buttons/IconButton/IconButton";
import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton";
import { Icon } from "../../Icon/Icon";
import HorizontalScroller from "../../Scroller/HorizontalScroller";
import { ReactComponent as Category } from "../../../assets/images/svg/category.svg";
import { ReactComponent as Subcategory } from "../../../assets/images/svg/subcategory.svg";
import { ReactComponent as Quantity } from "../../../assets/images/svg/quantity.svg";
@@ -149,31 +148,6 @@ export const Info = styled(Box)`
left: 5px;
}
`;
export const OfferTitle = styled(Typography)`
font-family: "Open Sans";
flex: 1;
color: ${selectedTheme.primaryPurple};
font-weight: 700;
font-size: 24px;
padding: 0 60px;
@media screen and (max-width: 600px) {
padding: 0;
font-size: 18px;
}
`;
export const OfferImage = styled.img`
min-width: 144px;
min-height: 144px;
width: 144px;
height: 144px;
margin-right: 20px;
object-fit: cover;

@media screen and (max-width: 600px) {
min-width: 144px;
margin-right: 13px;
}
`;
export const OfferAuthor = styled(Box)`
display: flex;
flex: 1;
@@ -191,16 +165,7 @@ export const OfferLocation = styled(Typography)`
line-height: 16px;
font-size: 12px;
`;
export const OfferDetails = styled(Box)`
display: flex;
flex-direction: column;
flex-wrap: ${(props) => (!props.halfwidth ? "no-wrap" : "wrap")};
justify-content: space-between;
padding: 0 60px;
@media (max-width: 600px) {
padding: 0;
}
`;

export const OfferCategory = styled(Box)`
font-family: "Open Sans";
color: ${selectedTheme.primaryText};
@@ -318,14 +283,6 @@ export const Details = styled(Box)`
`;
// export const OfferImage = styled.img`
// `
export const Scroller = styled(HorizontalScroller)`
min-height: 144px;
min-width: 144px;
max-width: 100%;
/* & div {
margin: 0 9px;
} */
`;

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

+ 0
- 36
src/components/Cards/ItemDetailsCard/MockupOfferDetails.js Прегледај датотеку

@@ -1,36 +0,0 @@
import React from "react"
import {ReactComponent as DummyImage1 } from "../../../assets/images/svg/dummyImages/offer-1.svg"

export const Offer = {
id: 0,
title: "Vino",
category: "Hrana i pice",
subcategory:"Farbe",
status:"novo",
quantity:150,
numberOfViews:45,
description: "Vinarija Aleksić osnovana je u Vranju 2006. godine, otvorivši time put oživljavanju vinogradarstva na jugu Srbije.Vinarija Aleksić osnovana je u Vranju 2006. godine, otvorivši time put oživljavanju vinogradarstva na jugu Srbije.Vinarija Aleksić osnovana je u Vranju 2006. godine, otvorivši time put oživljavanju vinogradarstva na jugu Srbije.Vinarija Aleksić osnovana je u Vranju 2006. godine, otvorivši time put oživljavanju vinogradarstva na jugu Srbije.",
images: [
{
id:0,
image: <DummyImage1 />
},
{
id:1,
image: <DummyImage1 />
},
{
id:2,
image: <DummyImage1 />
},
{
id:3,
image: <DummyImage1 />
},
{
id:4,
image: <DummyImage1 />
},
],
postDate: "12.04.2022",
}

+ 41
- 9
src/components/Cards/ItemDetailsCard/OfferDetails/OfferDetails.js Прегледај датотеку

@@ -1,14 +1,46 @@
import React from 'react'
import PropTypes from 'prop-types'
import React from "react";
import PropTypes from "prop-types";
import {
Details,
OfferDescriptionText,
OfferDescriptionTitle,
OfferImage,
OfferLittleDetails,
OfferTitle,
Scroller,
} from "./OfferDetails.styled";
import { useTranslation } from "react-i18next";

const OfferDetails = () => {
const OfferDetails = (props) => {
const offer = props.offer;
const { t } = useTranslation();
return (
<div>OfferDetails</div>
)
}
<Details
hasScrollBar={!props.showPublishButton}
exchange={props.showExchangeButton}
>
<OfferTitle>{offer?.offer?.name}</OfferTitle>
<Scroller>
{offer?.offer?.images?.map((item) => {
return <OfferImage src={item} key={item} />;
})}
</Scroller>
<OfferLittleDetails>
<OfferDescriptionTitle>
{t("itemDetailsCard.description")}
</OfferDescriptionTitle>
<OfferDescriptionText showBarterButton={props.showExchangeButton}>
{offer?.offer?.description}
</OfferDescriptionText>
</OfferLittleDetails>
</Details>
);
};

OfferDetails.propTypes = {
offer: PropTypes.any,
}
offer: PropTypes.any,
showExchangeButton: PropTypes.bool,
showPublishButton: PropTypes.bool,
};

export default OfferDetails
export default OfferDetails;

+ 101
- 0
src/components/Cards/ItemDetailsCard/OfferDetails/OfferDetails.styled.js Прегледај датотеку

@@ -0,0 +1,101 @@
import { Box, Typography } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../../themes";
import HorizontalScroller from "../../../Scroller/HorizontalScroller";

export const Details = styled(Box)`
display: flex;
flex-direction: column;
gap: 12px;
${(props) => props.hasScrollBar && !props.exchange && `height: 300px;`}
overflow-y: auto;
overflow-x: hidden;
::-webkit-scrollbar {
width: 5px;
}
::-webkit-scrollbar-thumb {
background: #c4c4c4;
border-radius: 3px;
}

@media screen and (max-width: 600px) {
margin-top: 15px;
${(props) =>
!props.hasScrollBar && props.exchange &&
`
overflow: hidden;
max-height: none;`}
}
`;

export const OfferTitle = styled(Typography)`
font-family: "Open Sans";
flex: 1;
color: ${selectedTheme.primaryPurple};
font-weight: 700;
font-size: 24px;
padding: 0 60px;
@media screen and (max-width: 600px) {
padding: 0;
font-size: 18px;
}
`;
export const OfferLittleDetails = styled(Box)`
display: flex;
flex-direction: column;
flex-wrap: ${(props) => (!props.halfwidth ? "no-wrap" : "wrap")};
justify-content: space-between;
padding: 0 60px;
@media (max-width: 600px) {
padding: 0;
}
`;
export const Scroller = styled(HorizontalScroller)`
min-height: 144px;
min-width: 144px;
max-width: 100%;
/* & div {
margin: 0 9px;
} */
`;
export const OfferDescriptionTitle = styled(Box)`
font-family: "Open Sans";
font-size: 12px;
color: ${selectedTheme.primaryDarkText};
line-height: 16px;
@media (max-width: 600px) {
font-size: 9px;
line-height: 13px;
}
`;
export const OfferDescriptionText = styled(Box)`
font-family: "Open Sans";
font-size: 16px;
color: ${selectedTheme.primaryDarkText};
line-height: 22px;
padding-bottom: 20px;
max-width: ${(props) => props.showBarterButton ? "calc(100% - 230px)" : "100%"};
@media (max-width: 600px) {
font-size: 14px;
max-width: 100%;
max-height: 100px;
}
/* max-width: calc(100% - 230px); */
/* overflow: hidden; */
/* display: -webkit-box;
-webkit-line-clamp: 5;
-webkit-box-orient: vertical; */
`;
export const OfferImage = styled.img`
min-width: 144px;
min-height: 144px;
width: 144px;
height: 144px;
margin-right: 20px;
object-fit: cover;

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

+ 6
- 4
src/components/Cards/MessageCard/MessageCard.js Прегледај датотеку

@@ -11,14 +11,16 @@ import { formatDateTime } from "../../../util/helpers/dateHelpers";

const MessageCard = (props) => {
const message = props.message;
const dateString = formatDateTime(new Date(message._created))
const dateString = formatDateTime(new Date(message._created));
return (
<MessageCardContainer isMyMessage={props.isMyMessage}>
<ProfileImage src={props.image} />
<MessageContent isMyMessage={props.isMyMessage}>
<MessageText isMyMessage={props.isMyMessage} >{props.message.text}</MessageText>
<MessageDate isMyMessage={props.isMyMessage} >{dateString}</MessageDate>
<MessageText isMyMessage={props.isMyMessage}>
{props.message.text}
</MessageText>
<MessageDate isMyMessage={props.isMyMessage}>{dateString}</MessageDate>
</MessageContent>
</MessageCardContainer>
);

+ 16
- 2
src/components/Cards/OfferCard/OfferCard.js Прегледај датотеку

@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useMemo, useState } from "react";
import PropTypes from "prop-types";
import {
CheckButton,
@@ -36,11 +36,14 @@ import { ReactComponent as Message } from "../../../assets/images/svg/mail.svg";
import selectedTheme from "../../../themes";
import { useHistory } from "react-router-dom";
import CreateOffer from "../CreateOfferCard/CreateOffer";
import { useSelector } from "react-redux";
import { selectUserId } from "../../../store/selectors/loginSelectors";

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

const routeToItem = (itemId) => {
history.push(`/proizvodi/${itemId}`);
@@ -62,6 +65,13 @@ const OfferCard = (props) => {
setEditOfferModal(false);
};

const showMessageIcon = useMemo(() => {
if (userId === props.offer?.userId) {
return false;
}
return true;
}, [userId, props.offer]);

if (deleteOfferModal || editOfferModal) {
document.body.style.overflow = "hidden";
} else {
@@ -175,7 +185,11 @@ const OfferCard = (props) => {
<StarIcon disabled={props.disabledReviews} />
</StarIconContainer>
) : (
<MessageIcon vertical={props.vertical} onClick={messageUser}>
<MessageIcon
showMessageIcon={showMessageIcon}
vertical={props.vertical}
onClick={messageUser}
>
<Message />
</MessageIcon>
)}

+ 7
- 2
src/components/Cards/OfferCard/OfferCard.styled.js Прегледај датотеку

@@ -254,6 +254,7 @@ export const CheckButton = styled(PrimaryButton)`
}
`;
export const MessageIcon = styled(IconButton)`
${(props) => !props.showMessageIcon && "display: none;"}
width: 40px;
height: 40px;
position: absolute;
@@ -331,12 +332,16 @@ export const RemoveIconContainer = styled(MessageIcon)`
`;
export const RemoveIcon = styled(Remove)``;
export const EditIconContainer = styled(MessageIcon)`
display: block;
right: 70px;
`;
export const EditIcon = styled(Edit)``;
export const StarIconContainer = styled(MessageIcon)`
opacity: ${props => props.disabled ? "0.4" : "1"};
${props => props.disabled && `
display: block;
opacity: ${(props) => (props.disabled ? "0.4" : "1")};
${(props) =>
props.disabled &&
`
cursor: initial;
& button {
cursor: initial;

+ 0
- 24
src/components/Cards/UserReviewsCard/Mockupdata.js Прегледај датотеку

@@ -1,24 +0,0 @@
export default [{
id: 0,
name: "Coca-Cola",
quote: "Odlična saradnja. Sve preporuke za kompaniju",
isGood: true,
isGoodCommunication: "DA",
isSuccessfulSwap: "DA"
}
,{
id: 1,
name: "Voda Vrnjci",
quote: "Sasvim korektna saradnja, rado bih ponovio poslovanje sa Vama.",
isGood: true,
isGoodCommunication: "DA",
isSuccessfulSwap: "DA"
}
,{
id: 2,
name: "Women's Beauty House",
quote: "Nismo se najbolje razumeli, ali generalno ok",
isGood: false,
isGoodCommunication: "NE",
isSuccessfulSwap: "NE"
}];

+ 12
- 36
src/components/Cards/UserReviewsCard/UserReviewsCard.js Прегледај датотеку

@@ -15,56 +15,35 @@ import {
ThumbDown,
ThumbUp,
} from "./UserReviewsCard.styled";

import { ListItem } from "@mui/material";
import selectedTheme from "../../../themes";
import { useTranslation } from "react-i18next";
import { reviewEnum } from "../../../enums/reviewEnum";
// import { useDispatch } from "react-redux";
// import { fetchProfile } from "../../../store/actions/profile/profileActions";

const UserReviewsCard = (props) => {
const { t } = useTranslation();

// const dispatch = useDispatch();

// useEffect(() => {
// if (props.review?.userId) {
// dispatch(fetchProfile(props.review.userId));
// }
// }, [props.review?.userId])
console.log(props);

const review = useMemo(() => {
if (props.givingReview) {
return {
...props.review
}
...props.review,
};
}
let isSuccessfulSwap = "DA";
if (props.review.succeeded === "failed") isSuccessfulSwap = "NE";
let isGoodCommunication = "DA";
if (props.review.communication === "could be better") isGoodCommunication = "MOŽE BOLJE";
if (props.review.communication === "could be better")
isGoodCommunication = "MOŽE BOLJE";
if (props.review.communication === "no") isGoodCommunication = "NE";
return {
name: props.review.companyName,
image: props.review.image,
isGoodCommunication,
isSuccessfulSwap,
quote: props?.review?.message
}
quote: props?.review?.message,
};
}, [props.review]);

const isGood = useMemo(() => {
if (
review?.isGoodCommunication === reviewEnum.NO.mainText ||
review?.isSuccessfulSwap === reviewEnum.NO.mainText
) {
return false;
}
return true;
}, [review]);

return (
<ReviewContainer key={review?.image}>
<ListItem alignItems="flex-start" sx={{ alignItems: "center", mt: 2 }}>
@@ -84,17 +63,14 @@ const UserReviewsCard = (props) => {
sx={{ pl: 2, py: 2 }}
>
<ThumbBox item>
{isGood ? <ThumbUp color="success" /> : <ThumbDown color="error" />}
{review.isSuccessfulSwap ? (
<ThumbUp color="success" />
) : (
<ThumbDown color="error" />
)}
</ThumbBox>
<ReviewQuoteBox item>
<ReviewQuoteText
sx={{ display: "inline" }}
component="span"
variant="body2"
color="text.primary"
>
&quot;{review?.quote}&quot;
</ReviewQuoteText>
<ReviewQuoteText>&quot;{review?.quote}&quot;</ReviewQuoteText>
</ReviewQuoteBox>
</ReviewQuote>
<ReviewDetails sx={{ pl: 2, pb: 2 }}>

+ 3
- 3
src/components/ChatColumn/ChatColumn.js Прегледај датотеку

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect } from "react";
import ChatCard from "../Cards/ChatCard/ChatCard";
import {
ChatColumnContainer,
@@ -36,7 +36,7 @@ export const ChatColumn = () => {

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

useEffect(() => {
setSortOption(sorting.selectedSortOption);
@@ -82,7 +82,7 @@ export const ChatColumn = () => {
})}
</HeaderSelect>
</TitleSortContainer>
<ListHeader enableSort={true}></ListHeader>
<ListHeader enableSort={true} />
<ListContainer>
{chats.map((item, index) => (
<ChatCard key={index} chat={item} />

+ 4
- 14
src/components/CreateReview/CreateReview.js Прегледај датотеку

@@ -9,15 +9,14 @@ import {
} from "./CreateReview.styled";
import FirstStepCreateReview from "./FirstStep/FirstStepCreateReview";
import SecondStepCreateReview from "./SecondStep/SecondStepCreateReview";
import ThirdStepCreateReview from "./ThirdStep/ThirdStepCreateReview";
import { useDispatch, useSelector } from "react-redux";
import { giveReview } from "../../store/actions/review/reviewActions";
import { selectUserId } from "../../store/selectors/loginSelectors";
import { reviewEnum } from "../../enums/reviewEnum";
import ThirdStepCreateReview from "./ThirdStep/ThirdStepCreateReview";

const CreateReview = (props) => {
const offer = props.offer;
console.log("props aaa: ", props);
const [informations, setInformations] = useState({});
const [currentStep, setCurrentStep] = useState(1);
const dispatch = useDispatch();
@@ -29,15 +28,12 @@ const CreateReview = (props) => {
props.handleGiveReviewSuccess();
};
const submitForm = () => {
let communication;
if (informations.correctCommunication === reviewEnum.YES.mainText)
communication = "yes";
let communication = "yes";
if (informations.correctCommunication === reviewEnum.NO.mainText)
communication = "no";
if (informations.correctCommunication === reviewEnum.NOT_BAD.mainText)
communication = "could be better";
let succeeded;
succeeded = "failed";
let succeeded = "failed";
if (informations.exchangeSucceed === reviewEnum.YES.mainText)
succeeded = "succeeded";
dispatch(
@@ -55,10 +51,6 @@ const CreateReview = (props) => {
};
const goToNextStep = (newInformations) => {
setInformations((prevInformations) => {
console.log({
...prevInformations,
...newInformations,
});
return {
...prevInformations,
...newInformations,
@@ -87,12 +79,10 @@ const CreateReview = (props) => {
<CloseButton onClick={closeModal}>
<CloseIcon />
</CloseButton>
{currentStep === 2 ? (
{currentStep === 2 && (
<BackIcon onClick={goToPrevStep}>
<ArrowBackIcon />
</BackIcon>
) : (
""
)}
{currentStep === 1 && (
<FirstStepCreateReview

+ 4
- 2
src/components/CreateReview/FirstStep/FirstStepCreateReview.js Прегледај датотеку

@@ -89,14 +89,16 @@ const FirstStepCreateReview = (props) => {
formik.setFieldValue("exchangeSucceed", event.target.value.mainText)
}
>
{Object.keys(reviewEnum).map((property) => (
{Object.keys(reviewEnum).map((property) => {
if (property === "NOT_BAD") return;
return (
<SelectOption
key={reviewEnum[property].value}
value={reviewEnum[property]}
>
{reviewEnum[property].mainText}
</SelectOption>
))}
)})}
</SelectField>

<FieldLabel leftText={t("reviews.comment")} />

+ 13
- 18
src/components/DirectChat/DirectChat.js Прегледај датотеку

@@ -4,11 +4,9 @@ import { DirectChatContainer } from "./DirectChat.styled";
import DirectChatHeaderTitle from "./DirectChatHeaderTitle/DirectChatHeaderTitle";
import DirectChatHeader from "./DirectChatHeader/DirectChatHeader";
import { useDispatch, useSelector } from "react-redux";
// import { fetchOneOffer } from "../../store/actions/offers/offersActions";
import { useLocation, useRouteMatch } from "react-router-dom";
import { fetchOneChat } from "../../store/actions/chat/chatActions";
import { fetchOneChat, setOneChat } from "../../store/actions/chat/chatActions";
import {
// selectLatestChats,
selectSelectedChat,
} from "../../store/selectors/chatSelectors";
import DirectChatContent from "./DirectChatContent/DirectChatContent";
@@ -20,46 +18,43 @@ const DirectChat = () => {
const offer = useSelector(selectOffer);
const routeMatch = useRouteMatch();
const location = useLocation();
// const allChats = useSelector(selectLatestChats);
// const foundChat = useMemo(
// () => allChats.find((item) => item?.chat?._id === chat?.chat?._id),
// [chat, allChats]
// );
const dispatch = useDispatch();

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

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

const interlocutorObject = useMemo(() => {
if (location?.state?.offerId) {
return {
image: offer?.companyData?.image,
name: offer?.companyData?.company?.name,
location: offer?.companyData?.company?.contacts?.location
}
location: offer?.companyData?.company?.contacts?.location,
};
}
return chat?.interlocutor;
}, [chat,location.state, offer]);
console.log("offerObject: ", offerObject);
console.log("chatObject: ", chatObject);
console.log("interlucatorObject: ", interlocutorObject);
const dispatch = useDispatch();
}, [chat, location.state, offer]);

useEffect(() => {
console.log(location.state)
if (routeMatch.params.idChat && location.state?.offerId) {
if (routeMatch.params.idChat) {
refreshChat();
}
}, [routeMatch.params.idChat, location.state?.offerId]);

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

+ 3
- 3
src/components/DirectChat/DirectChatHeader/DirectChatHeader.js Прегледај датотеку

@@ -32,15 +32,15 @@ const DirectChatHeader = (props) => {
}
return false;
}, [exchange, userId])
const refetchExchange = () => {
dispatch(fetchExchange(chat.chat.exchangeId));
}
const makeReview = () => {
setShowReviewModal(true);
};
const handleGiveReviewSuccess = () => {
refetchExchange();
}
const refetchExchange = () => {
dispatch(fetchExchange(chat.chat.exchangeId));
}
return (
<DirectChatHeaderContainer>
{showReviewModal && (

+ 16
- 12
src/components/DirectChat/DirectChatHeaderTitle/DirectChatHeaderTitle.js Прегледај датотеку

@@ -1,20 +1,24 @@
import React from 'react'
import PropTypes from 'prop-types'
import { DirectChatHeaderTitleContainer, HeaderTitleContent, MessageIcon } from './DirectChatHeaderTitle.styled'
import { useTranslation } from 'react-i18next'
import React from "react";
import PropTypes from "prop-types";
import {
DirectChatHeaderTitleContainer,
HeaderTitleContent,
MessageIcon,
} from "./DirectChatHeaderTitle.styled";
import { useTranslation } from "react-i18next";

const DirectChatHeaderTitle = () => {
const {t} = useTranslation();
const { t } = useTranslation();
return (
<DirectChatHeaderTitleContainer>
<MessageIcon />
<HeaderTitleContent>{t("messages.headerTitle")}</HeaderTitleContent>
<MessageIcon />
<HeaderTitleContent>{t("messages.headerTitle")}</HeaderTitleContent>
</DirectChatHeaderTitleContainer>
)
}
);
};

DirectChatHeaderTitle.propTypes = {
children: PropTypes.node,
}
children: PropTypes.node,
};

export default DirectChatHeaderTitle
export default DirectChatHeaderTitle;

+ 9
- 5
src/components/DirectChat/DirectChatNewMessage/DirectChatNewMessage.js Прегледај датотеку

@@ -8,7 +8,10 @@ import {
import { useTranslation } from "react-i18next";
import selectedTheme from "../../../themes";
import { useDispatch } from "react-redux";
import { sendMessage, startNewChat } from "../../../store/actions/chat/chatActions";
import {
sendMessage,
startNewChat,
} from "../../../store/actions/chat/chatActions";
import { useHistory, useLocation } from "react-router-dom";

const DirectChatNewMessage = (props) => {
@@ -35,13 +38,14 @@ const DirectChatNewMessage = (props) => {
setTypedValue("");
};
const handleMessageSendSuccess = (newChatId) => {
console.log("NEW CHAT ID: ", newChatId);
history.replace(`${newChatId}`);
}
};
const initiateNewChat = (typedValue) => {
const offerId = location.state.offerId;
dispatch(startNewChat({offerId, message: typedValue, handleMessageSendSuccess}))
}
dispatch(
startNewChat({ offerId, message: typedValue, handleMessageSendSuccess })
);
};
return (
<DirectChatNewMessageContainer>
<NewMessageField

+ 18
- 36
src/components/DirectChat/MiniChatColumn/MiniChatColumn.js Прегледај датотеку

@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from "react";
import React, { useEffect, useMemo } from "react";
import PropTypes from "prop-types";
import { MiniChatColumnContainer } from "./MiniChatColumn.styled";
import MiniChatCard from "../../Cards/MiniChatCard/MiniChatCard";
@@ -14,9 +14,7 @@ import { selectOffer } from "../../../store/selectors/offersSelectors";

const MiniChatColumn = () => {
const chats = useSelector(selectLatestChats);
const [chatsToShow, setChatsToShow] = useState([]);
const selectedChat = useSelector(selectSelectedChat);
const [isThereNewChat, setIsThereNewChat] = useState(false);
const offer = useSelector(selectOffer);
const location = useLocation();
const dispatch = useDispatch();
@@ -25,29 +23,15 @@ const MiniChatColumn = () => {
return {
interlocutorData: {
image: offer?.companyData?.image,
name: offer?.companyData?.company?.name
name: offer?.companyData?.company?.name,
},
offerData: {
name: offer?.offer?.name
}
}
}
return {}
}, [offer, location.state])
useEffect(() => {
if (location.state?.offerId) {
setIsThereNewChat(true);
} else {
if (isThereNewChat !== false) {
dispatch(fetchChats());
setIsThereNewChat(false);
}
name: offer?.offer?.name,
},
};
}
}, [location.state])

useEffect(() => {
setChatsToShow([...chats]);
}, [chats])
return {};
}, [offer, location.state]);

useEffect(() => {
dispatch(fetchChats());
@@ -55,20 +39,18 @@ const MiniChatColumn = () => {
return (
<MiniChatColumnContainer>
<MiniChatColumnHeader />
{isThereNewChat && (
<MiniChatCard
chat={newChat}
selected
/>
)}
{chatsToShow.map((item) => {
{location.state?.offerId && <MiniChatCard chat={newChat} selected />}
{chats.map((item) => {
return (
<MiniChatCard
key={Date.now() * Math.random()}
chat={item}
selected={item?.chat?._id === selectedChat?.chat?._id && !isThereNewChat}
/>
)})}
<MiniChatCard
key={Date.now() * Math.random()}
chat={item}
selected={
item?.chat?._id === selectedChat?.chat?._id
}
/>
);
})}
</MiniChatColumnContainer>
);
};

+ 18
- 12
src/components/DirectChat/MiniChatColumn/MiniChatColumnHeader/MiniChatColumnHeaderTitle.js Прегледај датотеку

@@ -1,20 +1,26 @@
import React from 'react'
import PropTypes from 'prop-types'
import { HeaderTitleContent, MailIcon, MiniChatColumnHeaderContainer } from './MiniChatColumnHeaderTitle.styled'
import { useTranslation } from 'react-i18next'
import React from "react";
import PropTypes from "prop-types";
import {
HeaderTitleContent,
MailIcon,
MiniChatColumnHeaderContainer,
} from "./MiniChatColumnHeaderTitle.styled";
import { useTranslation } from "react-i18next";

const MiniChatColumnHeader = () => {
const {t} = useTranslation();
const { t } = useTranslation();
return (
<MiniChatColumnHeaderContainer>
<MailIcon/>
<HeaderTitleContent>{t("messages.miniChatHeaderTitle")}</HeaderTitleContent>
<MailIcon />
<HeaderTitleContent>
{t("messages.miniChatHeaderTitle")}
</HeaderTitleContent>
</MiniChatColumnHeaderContainer>
)
}
);
};

MiniChatColumnHeader.propTypes = {
children: PropTypes.node,
}
children: PropTypes.node,
};

export default MiniChatColumnHeader
export default MiniChatColumnHeader;

+ 17
- 14
src/components/Dropdown/DropdownList/DropdownList.js Прегледај датотеку

@@ -15,20 +15,16 @@ import PropTypes from "prop-types";
const DropdownList = (props) => {
const [listShown, setListShown] = useState(props.defaultOpen);
useEffect(() => {
if (props.open !== null || props.open !== undefined) {
if (props.open !== null || props.open !== undefined)
setListShown(props.open);
}
}, [props.open]);
const handleShow = () => {
if (props.setIsOpened) {
if (props.setIsOpened)
props.setIsOpened(!listShown);
}
if (!props.disabled) {
if (!props.disabled)
setListShown((prevState) => !prevState);
if (!(props.open !== null || props.open !== undefined))
setListShown((prevState) => !prevState);
}
if (!(props.open !== null || props.open !== undefined)) {
setListShown(prevState => !prevState)
}
};
return (
<DropdownListContainer fullwidth={props.fullWidth ? 1 : 0}>
@@ -48,7 +44,11 @@ const DropdownList = (props) => {
>
{props.title}
</DropdownTitle>
{(props.open !== null && props.open !== undefined ? props.open : listShown) ? (
{(
props.open !== null && props.open !== undefined
? props.open
: listShown
) ? (
<ToggleIconOpened
style={props.toggleIconStyles}
onClick={!props.disabled ? () => handleShow() : () => {}}
@@ -65,16 +65,20 @@ const DropdownList = (props) => {
</ToggleIconClosed>
)}
</DropdownHeader>
<ToggleContainer shouldShow={props.open !== null && props.open !== undefined ? props.open : listShown}>
<ToggleContainer
shouldShow={
props.open !== null && props.open !== undefined
? props.open
: listShown
}
>
<DropdownOptions>{props.headerOptions}</DropdownOptions>
<ListContainer>{props.children}</ListContainer>
</ToggleContainer>
</DropdownListContainer>
);
};

export default DropdownList;

DropdownList.propTypes = {
title: PropTypes.string,
dropdownIcon: PropTypes.node,
@@ -90,7 +94,6 @@ DropdownList.propTypes = {
open: PropTypes.bool,
disabled: PropTypes.bool,
};

DropdownList.defaultProps = {
fullWidth: false,
defaultOpen: false,

+ 8
- 3
src/components/Header/Drawer/Drawer.js Прегледај датотеку

@@ -21,7 +21,7 @@ import {
ToolsContainer,
UserIcon,
} from "./Drawer.styled";
import { useSelector } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { selectUserId } from "../../../store/selectors/loginSelectors";
import selectedTheme from "../../../themes";
import { IconButton } from "../../Buttons/IconButton/IconButton";
@@ -29,12 +29,14 @@ import { useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
import { CHAT_PAGE, LOGIN_PAGE, MY_OFFERS_PAGE, REGISTER_PAGE } from "../../../constants/pages";
import { selectProfileName } from "../../../store/selectors/profileSelectors";
import { logoutUser } from "../../../store/actions/login/loginActions";

export const Drawer = (props) => {
const user = useSelector(selectUserId);
const { t } = useTranslation();
const history = useHistory();
const name = useSelector(selectProfileName);
const dispatch = useDispatch();

const goToMyPosts = () => {
props.toggleDrawer();
@@ -60,7 +62,10 @@ export const Drawer = (props) => {
props.toggleDrawer();
props.addOffer();
}
const logoutUser = () => {};
const logout = () => {
props.toggleDrawer();
dispatch(logoutUser());
};
return (
<DrawerContainer>
<CloseButton onClick={props.toggleDrawer}>
@@ -101,7 +106,7 @@ export const Drawer = (props) => {
{t("header.addOffer")}
</AddOfferButton>
<LogoutButton>
<IconButton onClick={logoutUser}>
<IconButton onClick={logout}>
<LogoutIcon />
</IconButton>
<LogoutText>{t("common.logout")}</LogoutText>

+ 0
- 1
src/components/Header/Header.js Прегледај датотеку

@@ -183,7 +183,6 @@ const Header = (props) => {
searchRef.current.removeEventListener("keyup", listener);
};
const handleSearch = (value) => {
if (value.length === 0) return;
search.searchOffers(value);
};
const toggleFilters = () => {

+ 12
- 21
src/components/ImagePicker/ImagePicker.js Прегледај датотеку

@@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from "react";
import React, { useCallback, useEffect, useRef, useState } from "react";
import PropTypes from "prop-types";
import {
AddFile,
@@ -12,8 +12,6 @@ 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 { Input } from "@mui/material";

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

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

let listener;
useEffect(() => {
listener = (event) => {
if (imageRef.current) {
if (imageRef.current.contains(event.target)) {
setIsEditing(true);
} else {
setIsEditing(false);
}
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);
}, [imageRef]);

}, []);
const handleChange = () => {
fileInputRef.current.value = "";
fileInputRef.current.click();
@@ -57,7 +51,6 @@ const ImagePicker = (props) => {
console.log(error);
};
};

const handleDelete = () => {
if (props.deleteImage) props.deleteImage();
setImage("");
@@ -96,7 +89,6 @@ const ImagePicker = (props) => {
</ImagePickerContainer>
);
};

ImagePicker.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
@@ -105,5 +97,4 @@ ImagePicker.propTypes = {
deleteImage: PropTypes.func,
showDeleteIcon: PropTypes.bool,
};

export default ImagePicker;

+ 0
- 187
src/components/InputFields/BaseInputField.js Прегледај датотеку

@@ -1,187 +0,0 @@
import React, { useEffect, useState, useRef } from 'react';
import PropTypes from 'prop-types';
import { ErrorMessage } from 'formik';
import IconButton from '../IconButton/IconButton';
import { ReactComponent as Search } from '../../assets/images/svg/search.svg';
import { ReactComponent as EyeOn } from '../../assets/images/svg/eye-on.svg';
import { ReactComponent as EyeOff } from '../../assets/images/svg/eye-off.svg';
import { ReactComponent as CapsLock } from '../../assets/images/svg/caps-lock.svg';

const BaseInputField = ({
type,
label,
field,
form,
placeholder,
clearPlaceholderOnFocus = true,
isSearch,
className,
disabled,
centerText,
link,
errorMessage,
autoFocus,
isCapsLockOn,
...props
}) => {
const [inputPlaceholder, setPlaceholder] = useState(placeholder);

const inputField = useRef(null);

useEffect(() => {
if (autoFocus) {
inputField.current.focus();
}
}, [autoFocus, inputField]);

useEffect(() => {
if (errorMessage) {
form.setFieldError(field.name, errorMessage);
}
}, [errorMessage]); // eslint-disable-line

useEffect(() => {
setPlaceholder(placeholder);
}, [placeholder]);

const [inputType, setInputType] = useState('password');
const passwordInput = type === 'password' ? ' c-input--password' : '';

const showPassword = () => {
if (inputType === 'password') {
setInputType('text');
} else {
setInputType('password');
}
};

// Nester Formik Field Names get bugged because of Undefined values, so i had to fix it like this
// If you ask why 0 and 1? I dont see a need for forms to be nested more then 2 levels?
const fieldName = field.name.split('.');

const formError =
fieldName[0] && fieldName[1]
? form.errors[fieldName[0]] && form.errors[fieldName[0]][fieldName[1]]
: form.errors[fieldName[0]];

const formTouched =
fieldName[0] && fieldName[1]
? form.touched[fieldName[0]] && form.touched[fieldName[0]][fieldName[1]]
: form.touched[fieldName[0]];

function styles() {
let style = 'c-input';

if (formError && formTouched) {
style += ` c-input--error`;
}

if (type === 'password') {
style += ` c-input--password`;
}

if (isSearch) {
style += ` c-input--search`;
}

if (centerText) {
style += ` c-input--center-text`;
}

if (type === 'number') {
style += ` c-input--demi-bold`;
}

if (className) {
style += ` ${className}`;
}

return style;
}

const additionalActions = () => {
if (!clearPlaceholderOnFocus) {
return null;
}

return {
onFocus: () => {
setPlaceholder('');
},
onBlur: (e) => {
setPlaceholder(placeholder);
field.onBlur(e);
},
};
};
return (
<div className={styles()}>
{!!label && (
<label className="c-input__label" htmlFor={field.name}>
{label}
</label>
)}
{link && <div className="c-input__link">{link}</div>}
<div className="c-input__field-wrap">
<input
ref={inputField}
type={type === 'password' ? inputType : type}
placeholder={inputPlaceholder}
disabled={disabled}
{...field}
{...props}
{...additionalActions()}
className="c-input__field"
/>
{!!isSearch && <Search className="c-input__icon" />}
{!!passwordInput && (
<>
{isCapsLockOn && <CapsLock className="c-input__caps-lock" />}
<IconButton
onClick={() => {
showPassword();
}}
className="c-input__icon"
>
{inputType === 'password' ? <EyeOff /> : <EyeOn />}
</IconButton>
</>
)}
</div>

<ErrorMessage name={field.name}>
{(errorMessage) => (
<span className="c-input__error">{errorMessage}</span>
)}
</ErrorMessage>
</div>
);
};
BaseInputField.propTypes = {
type: PropTypes.string,
field: PropTypes.shape({
name: PropTypes.string,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
}),
form: PropTypes.shape({
errors: PropTypes.shape({}),
setFieldError: PropTypes.func,
touched: PropTypes.shape({}),
}),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
disabled: PropTypes.bool,
isSearch: PropTypes.bool,
className: PropTypes.string,
link: PropTypes.node,
errorMessage: PropTypes.string,
centerText: PropTypes.bool,
clearPlaceholderOnFocus: PropTypes.bool,
demiBold: PropTypes.bool,
touched: PropTypes.bool,
autoFocus: PropTypes.bool,
isCapsLockOn: PropTypes.bool,
};

export default BaseInputField;

+ 0
- 40
src/components/InputFields/Checkbox.js Прегледај датотеку

@@ -1,40 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ReactComponent as Checked } from '../../assets/images/svg/checked.svg';
import { ReactComponent as Unchecked } from '../../assets/images/svg/unchecked.svg';

const Checkbox = ({ className, children, name, onChange, checked, field }) => (
<label htmlFor={name} className={`c-checkbox ${className || ''}`}>
<input
name={name}
id={name}
className="c-checkbox__field"
type="checkbox"
checked={checked}
{...field}
onChange={onChange || field.onChange}
/>

<div className="c-checkbox__indicator">
{checked ? (
<Checked className="c-checkbox__icon" />
) : (
<Unchecked className="c-checkbox__icon" />
)}
</div>
<div className="c-checkbox__text">{children}</div>
</label>
);

Checkbox.propTypes = {
children: PropTypes.node,
onChange: PropTypes.func,
checked: PropTypes.bool,
name: PropTypes.string,
field: PropTypes.shape({
onChange: PropTypes.func,
}),
className: PropTypes.string,
};

export default Checkbox;

+ 0
- 123
src/components/InputFields/CurrencyField.js Прегледај датотеку

@@ -1,123 +0,0 @@
import React, { useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { ErrorMessage, useField } from 'formik';
import CurrencyInput from 'react-currency-input-field';
import { formatMoneyNumeral } from '../../util/helpers/numeralHelpers';
import {
PLUS_SYMBOL,
MINUS_SYMBOL,
NUMPAD_MINUS_SYMBOL,
NUMPAD_PLUS_SYMBOL,
K_KEYCODE,
} from '../../constants/keyCodeConstants';

const CurrencyField = ({
autoFocus,
notCentered,
notBold,
label,
onChange,
value,
...props
}) => {
const [field, meta] = useField(props);
const inputField = useRef(null);
function styles() {
let style = 'c-currency-field';

if (meta.error && meta.touched) {
style += ` c-currency-field--error`;
}

if (notCentered) {
style += ` c-currency-field--not-centered`;
}

if (notBold) {
style += ` c-currency-field--not-bold`;
}

return style;
}

useEffect(() => {
if (autoFocus) {
inputField.current.focus();
}
}, [autoFocus, inputField]);

const onKeydownHandler = (event) => {
if (
event.keyCode === MINUS_SYMBOL ||
event.keyCode === PLUS_SYMBOL ||
event.keyCode === NUMPAD_MINUS_SYMBOL ||
event.keyCode === NUMPAD_PLUS_SYMBOL ||
event.keyCode === K_KEYCODE
) {
event.preventDefault();
}
};

const prefix = formatMoneyNumeral(0);
const prefixSymbol = () => {
if (prefix.includes('CAD')) {
return 'CAD ';
}

return '$';
};

return (
<div className={styles()}>
{!!label && (
<label className="c-currency-field__label" htmlFor={field.name}>
{label}
</label>
)}
{value ? (
<CurrencyInput
{...props}
prefix={prefixSymbol()}
onValueChange={(value) => {
onChange(value ? Number(value) : '');
}}
onKeyDown={(event) => onKeydownHandler(event)}
ref={inputField}
defaultValue={0}
value={value}
/>
) : (
<CurrencyInput
{...props}
prefix={prefixSymbol()}
onValueChange={(value) => {
onChange(value ? Number(value) : '');
}}
onKeyDown={(event) => onKeydownHandler(event)}
ref={inputField}
/>
)}
<ErrorMessage name={field.name}>
{(errorMessage) => (
<span className="c-currency-field__error">{errorMessage}</span>
)}
</ErrorMessage>
</div>
);
};

CurrencyField.propTypes = {
field: PropTypes.shape({
name: PropTypes.string,
}),
form: PropTypes.shape({}),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
disabled: PropTypes.bool,
onChange: PropTypes.func,
autoFocus: PropTypes.bool,
notCentered: PropTypes.bool,
notBold: PropTypes.bool,
value: PropTypes.number,
};

export default CurrencyField;

+ 0
- 33
src/components/InputFields/EmailField.js Прегледај датотеку

@@ -1,33 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';

import BaseInputField from './BaseInputField';

const EmailField = ({
field,
form,
label,
placeholder,
disabled,
...props
}) => (
<BaseInputField
type="email"
label={label}
placeholder={placeholder}
disabled={disabled}
form={form}
field={field}
{...props}
/>
);

EmailField.propTypes = {
field: PropTypes.shape({}),
form: PropTypes.shape({}),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
placeholder: PropTypes.string,
disabled: PropTypes.bool,
};

export default EmailField;

+ 0
- 74
src/components/InputFields/NumberField.js Прегледај датотеку

@@ -1,74 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';

import BaseInputField from './BaseInputField';
import {
PERIOD_SYMBOL,
COMMA_SYMBOL,
PLUS_SYMBOL,
MINUS_SYMBOL,
NUMPAD_PERIOD_SYMBOL,
NUMPAD_MINUS_SYMBOL,
NUMPAD_PLUS_SYMBOL,
DOWN_ARROW_KEYCODE,
UP_ARROW_KEYCODE,
} from '../../constants/keyCodeConstants';

const NumberField = ({
field,
form,
label,
placeholder,
disabled,
preventAllExceptNumbers,
...props
}) => {
const onKeydownHandler = (event) => {
if (preventAllExceptNumbers) {
if (
event.keyCode === PERIOD_SYMBOL ||
event.keyCode === COMMA_SYMBOL ||
event.keyCode === NUMPAD_PERIOD_SYMBOL ||
event.keyCode === DOWN_ARROW_KEYCODE ||
event.keyCode === UP_ARROW_KEYCODE
) {
event.preventDefault();
}
}

if (
event.keyCode === PLUS_SYMBOL ||
event.keyCode === MINUS_SYMBOL ||
event.keyCode === NUMPAD_MINUS_SYMBOL ||
event.keyCode === NUMPAD_PLUS_SYMBOL ||
event.keyCode === DOWN_ARROW_KEYCODE ||
event.keyCode === UP_ARROW_KEYCODE
) {
event.preventDefault();
}
};

return (
<BaseInputField
type="number"
label={label}
placeholder={placeholder}
disabled={disabled}
form={form}
field={field}
{...props}
onKeyDown={(event) => onKeydownHandler(event)}
/>
);
};

NumberField.propTypes = {
field: PropTypes.shape({}),
form: PropTypes.shape({}),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
placeholder: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
disabled: PropTypes.bool,
preventAllExceptNumbers: PropTypes.bool,
};

export default NumberField;

+ 0
- 74
src/components/InputFields/PasswordField.js Прегледај датотеку

@@ -1,74 +0,0 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';

import BaseInputField from './BaseInputField';
import PasswordStrength from './PasswordStrength';

const PasswordField = ({
field,
form,
label,
placeholder,
disabled,
shouldTestPasswordStrength,
autoFocus,
...props
}) => {
const [passwordValue, setPasswordValue] = useState('');
const [isCapsLockOn, setIsCapsLockOn] = useState(false);

const onChange = (e) => {
if (shouldTestPasswordStrength) {
const { value } = e.target;
setPasswordValue(value);
}

field.onChange(e);
};

const onKeyDown = (keyEvent) => {
if (keyEvent.getModifierState('CapsLock')) {
setIsCapsLockOn(true);
} else {
setIsCapsLockOn(false);
}
};

return (
<div className="c-password">
<BaseInputField
type="password"
label={label}
placeholder={placeholder}
disabled={disabled}
form={form}
field={field}
{...props}
onChange={onChange}
autoFocus={autoFocus}
onKeyDown={onKeyDown}
isCapsLockOn={isCapsLockOn}
/>
{shouldTestPasswordStrength && (
<PasswordStrength
passwordValue={passwordValue}
shouldTestPasswordStrength
/>
)}
</div>
);
};

PasswordField.propTypes = {
field: PropTypes.shape({
onChange: PropTypes.func,
}),
form: PropTypes.shape({}),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
placeholder: PropTypes.string,
disabled: PropTypes.bool,
shouldTestPasswordStrength: PropTypes.bool,
autoFocus: PropTypes.bool,
};

export default PasswordField;

+ 0
- 130
src/components/InputFields/PasswordStrength.js Прегледај датотеку

@@ -1,130 +0,0 @@
import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import owasp from 'owasp-password-strength-test';
import i18next from 'i18next';

owasp.config({
minOptionalTestsToPass: 3,
});

const passwordStrengthOptions = [
{
strength: 'weak',
color: '#FF5028',
},
{
strength: 'average',
color: '#FDB942',
},
{
strength: 'good',
color: '#06BEE7',
},
{
strength: 'strong',
color: '#00876A',
},
];

/**
* User must pass a required test and at least 3 optional.
* @param result - owasp result
* @returns {number} - index of password strength 0-3
*/
function getPasswordStrengthIndex(result) {
// requirement for strong password is required test passed and at least 3 optional tests
if (result.strong) {
return 3;
}

if (!result.strong && result.optionalTestsPassed >= 3) {
return 2;
}

if (result.optionalTestsPassed <= 0) {
return 0;
}

return result.optionalTestsPassed - 1;
}

const PasswordStrength = ({
shouldTestPasswordStrength,
passwordValue,
passwordStrengthTestsRequired,
}) => {
const strengthContainer = useRef(null);
const [passwordStrength, setPasswordStrength] = useState({
width: 0,
color: 'red',
});
const [error, setError] = useState('');

useEffect(() => {
if (shouldTestPasswordStrength && passwordValue) {
const bBox = strengthContainer.current.getBoundingClientRect();
const result = owasp.test(passwordValue);

const passwordStrengthIndex = getPasswordStrengthIndex(result);
const passwordOption = passwordStrengthOptions[passwordStrengthIndex];

const width = !passwordValue
? 0
: (bBox.width * (passwordStrengthIndex + 1)) /
passwordStrengthTestsRequired;

setPasswordStrength({ width, color: passwordOption.color });
const strength = i18next.t(`password.${passwordOption.strength}`);
setError(i18next.t('login.passwordStrength', { strength }));
}
}, [
passwordValue,
shouldTestPasswordStrength,
passwordStrengthTestsRequired,
]);

if (!shouldTestPasswordStrength || !passwordValue) {
return null;
}

const renderError = () => {
if (!error) {
return null;
}
return (
<div
className="c-input--error"
style={{
color: passwordStrength.color,
}}
>
{error}
</div>
);
};
return (
<div ref={strengthContainer} className="c-password-strength__container">
<div className="c-password-strength__line--wrapper">
<div
className="c-password-strength__line"
style={{
backgroundColor: passwordStrength.color,
width: passwordStrength.width,
}}
/>
</div>
{renderError()}
</div>
);
};
PasswordStrength.propTypes = {
shouldTestPasswordStrength: PropTypes.bool,
passwordValue: PropTypes.string,
passwordStrengthTestsRequired: PropTypes.number,
};

PasswordStrength.defaultProps = {
passwordStrengthTestsRequired: 4,
};

export default PasswordStrength;

+ 0
- 45
src/components/InputFields/PercentageField.js Прегледај датотеку

@@ -1,45 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import NumberFormat from 'react-number-format';

import TextField from './TextField';

const PercentageField = ({ field, ...props }) => {
const handleOnChange = (percentageField) => {
const { floatValue } = percentageField;

if (!props.onChange) {
throw Error('Provide an onChange handler');
}
if (floatValue > 100) {
return props.onChange('100');
}

if (floatValue <= 0 || !floatValue) {
return props.onChange('0');
}

return props.onChange(floatValue.toString());
};

return (
<NumberFormat
format="###%"
value={field.value}
customInput={TextField}
field={field}
{...props}
onValueChange={handleOnChange}
onChange={() => {}}
/>
);
};

PercentageField.propTypes = {
onChange: PropTypes.func,
field: PropTypes.shape({
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}),
};

export default PercentageField;

+ 0
- 49
src/components/InputFields/PhoneNumberField.js Прегледај датотеку

@@ -1,49 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ErrorMessage, useField } from 'formik';
import PhoneInput from 'react-phone-number-input';
import 'react-phone-number-input/style.css';

const PhoneNumberField = ({ label, ...props }) => {
const [field, meta] = useField(props);
const inputErrorClassName =
meta.error && meta.touched ? 'c-input--error' : '';

return (
<div className={`c-input c-phone-number ${inputErrorClassName}`}>
{!!label && (
<label className="c-input__label" htmlFor={field.name}>
{label}
</label>
)}
<PhoneInput
international
defaultCountry="US"
{...field}
{...props}
onChange={(value) => {
props.onPhoneChange(value);
}}
countryOptionsOrder={['US']}
/>
<ErrorMessage name={field.name}>
{(errorMessage) => (
<span className="c-input__error">{errorMessage}</span>
)}
</ErrorMessage>
</div>
);
};

PhoneNumberField.propTypes = {
field: PropTypes.shape({
name: PropTypes.string,
}),
form: PropTypes.shape({}),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
disabled: PropTypes.bool,
onChange: PropTypes.func,
onPhoneChange: PropTypes.func,
};

export default PhoneNumberField;

+ 0
- 54
src/components/InputFields/Radio.js Прегледај датотеку

@@ -1,54 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ReactComponent as RadioOn } from '../../assets/images/svg/radio-on.svg';
import { ReactComponent as RadioOff } from '../../assets/images/svg/radio-off.svg';

const Checkbox = ({
className,
children,
name,
checked,
field,
value,
selected,
id,
}) => (
<label
htmlFor={name}
className={`c-radio ${selected ? 'c-radio--selected' : ''} ${
className || ''
}`}
>
<input
name={name}
id={id}
className="c-radio__field"
type="radio"
checked={checked}
value={value}
{...field}
/>
<div className="c-radio__indicator">
{selected ? (
<RadioOn className="c-radio__icon" />
) : (
<RadioOff className="c-radio__icon" />
)}
</div>
<div className="c-radio__text">{children}</div>
</label>
);

Checkbox.propTypes = {
children: PropTypes.node,
checked: PropTypes.bool,
name: PropTypes.string,
field: PropTypes.shape({}),
form: PropTypes.shape({}),
className: PropTypes.string,
value: PropTypes.string,
selected: PropTypes.bool,
id: PropTypes.string,
};

export default Checkbox;

+ 0
- 37
src/components/InputFields/Search.js Прегледај датотеку

@@ -1,37 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';

import BaseInputField from './BaseInputField';

const Search = ({
field,
form,
label,
placeholder,
disabled,
className,
...props
}) => (
<BaseInputField
type="text"
label=""
placeholder={placeholder}
disabled={disabled}
form={form}
field={field}
isSearch
className={className}
{...props}
/>
);

Search.propTypes = {
field: PropTypes.shape({}),
form: PropTypes.shape({}),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
placeholder: PropTypes.string,
disabled: PropTypes.bool,
className: PropTypes.string,
};

export default Search;

+ 0
- 122
src/components/InputFields/SelectField.js Прегледај датотеку

@@ -1,122 +0,0 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import Select, { components, createFilter } from 'react-select';
import { ErrorMessage, useField } from 'formik';
import { ReactComponent as FilledChevronDown } from '../../assets/images/svg/filled-chevron-down.svg';

const SelectField = ({
label,
disabled,
options,
link,
defaultSelected = null,
dropdownFullHeight,
selectOption,
...props
}) => {
const [field, meta, helpers] = useField(props);

const filterConfig = {
ignoreCase: true,
ignoreAccents: true,
trim: true,
matchFrom: 'start',
};

useEffect(() => {
if (defaultSelected) {
helpers.setValue(defaultSelected);
}
}, [defaultSelected]); // eslint-disable-line

const DropdownIndicator = (props) =>
components.DropdownIndicator && (
<components.DropdownIndicator {...props}>
<FilledChevronDown />
</components.DropdownIndicator>
);

function styles() {
let style = 'c-input';

if (meta.error && meta.touched) {
style += ` c-input--error`;
}

if (dropdownFullHeight) {
style += ` c-input--dropdown-full-height`;
}

return style;
}

return (
<div className={styles()}>
{!!label && (
<label className="c-input__label" htmlFor={field.name}>
{label}
</label>
)}
{!!link && <div className="c-input__link">{link}</div>}
<Select
defaultValue={defaultSelected || options[0]}
components={{ DropdownIndicator }}
isSearchable={false}
classNamePrefix="c-select"
options={options}
isDisabled={disabled}
{...field}
{...props}
onBlur={(e) => {
helpers.setTouched(true);
field.onBlur(e);
}}
onChange={(selectedOption) => {
helpers.setValue(selectedOption);

if (props.onChange) {
props.onChange();
}

if (selectOption) {
selectOption(selectedOption);
}
}}
filterOption={createFilter(filterConfig)}
/>
<ErrorMessage name={field.name}>
{(errorMessage) => {
if (typeof errorMessage === 'string') {
return <span className="c-input__error">{errorMessage}</span>;
}
return <span className="c-input__error">{errorMessage.value}</span>;
}}
</ErrorMessage>
</div>
);
};

SelectField.propTypes = {
field: PropTypes.shape({
name: PropTypes.string,
}),
form: PropTypes.shape({}),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
disabled: PropTypes.bool,
options: PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}),
),
onChange: PropTypes.func,
link: PropTypes.node,
defaultSelected: PropTypes.shape({
label: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}),
dropdownFullHeight: PropTypes.bool,
selectOption: PropTypes.func,
};

export default SelectField;

+ 0
- 72
src/components/InputFields/TextField.js Прегледај датотеку

@@ -1,72 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';

import BaseInputField from './BaseInputField';
import {
BACKSPACE_KEYCODE,
TAB_KEYCODE,
RIGHT_ARROW_KEYCODE,
LEFT_ARROW_KEYCODE,
} from '../../constants/keyCodeConstants';

const TextField = ({
field,
form,
label,
placeholder,
disabled,
centerText,
autoFocus,
preventAllExceptNumbers,
...props
}) => {
const onKeydownHandler = (event) => {
if (preventAllExceptNumbers) {
if (
event.keyCode === BACKSPACE_KEYCODE ||
event.keyCode === TAB_KEYCODE ||
event.keyCode === RIGHT_ARROW_KEYCODE ||
event.keyCode === LEFT_ARROW_KEYCODE
) {
return;
}

if (
(event.keyCode < 58 && event.keyCode > 47) ||
(event.keyCode < 106 && event.keyCode > 95)
) {
return;
}

event.preventDefault();
}
};

return (
<BaseInputField
autoFocus={autoFocus}
type="text"
label={label}
placeholder={placeholder}
disabled={disabled}
form={form}
field={field}
centerText={centerText}
{...props}
onKeyDown={(event) => onKeydownHandler(event)}
/>
);
};

TextField.propTypes = {
field: PropTypes.shape({}),
form: PropTypes.shape({}),
label: PropTypes.string,
placeholder: PropTypes.string,
disabled: PropTypes.bool,
centerText: PropTypes.bool,
autoFocus: PropTypes.bool,
preventAllExceptNumbers: PropTypes.bool,
};

export default TextField;

+ 2
- 2
src/components/ItemDetails/Header/Header.js Прегледај датотеку

@@ -1,7 +1,6 @@
import React from "react";
import PropTypes from "prop-types";
import { useHistory } from "react-router-dom";
//import { IconButton } from "../../Buttons/IconButton/IconButton";
import { HeaderContainer, HeaderText, ButtonContainer } from "./Header.styled";
import { ArrowButton } from "../../Buttons/ArrowButton/ArrowButton";
import { useTranslation } from "react-i18next";
@@ -28,7 +27,8 @@ const Header = (props) => {
>
<ButtonContainer>
<ArrowButton side={"left"}></ArrowButton>
<HeaderText>{t("profile.back")}</HeaderText>
{/* <HeaderText>{t("profile.back")}</HeaderText> */}
<HeaderText>{t("itemDetailsCard.headerTitle")}</HeaderText>
</ButtonContainer>
</HeaderContainer>
);

+ 2
- 1
src/components/ItemDetails/ItemDetails.js Прегледај датотеку

@@ -6,12 +6,13 @@ import ItemDetailsCard from "../Cards/ItemDetailsCard/ItemDetailsCard";
import ItemDetailsHeaderCard from "./ItemDetailsHeaderCard/ItemDetailsHeaderCard";
import { selectOffer } from "../../store/selectors/offersSelectors";
import { selectUserId } from "../../store/selectors/loginSelectors";
// import { useHistory } from 'react-router-dom';

const ItemDetails = () => {
const offer = useSelector(selectOffer);
const userId = useSelector(selectUserId);
let isMyProfile = useMemo(() => {
if (offer?.offer?.userId?.toString() === userId.toString()) {
if (offer?.offer?.userId?.toString() === userId?.toString()) {
return true;
}
return false;

+ 8
- 60
src/components/ItemDetails/ItemDetailsHeaderCard/ItemDetailsHeaderCard.js Прегледај датотеку

@@ -1,49 +1,30 @@
import React from "react";
import PropTypes from "prop-types";
import {
DetailIcon,
DetailText,
MessageIcon,
OfferDetails,
OfferImage,
OfferTitle,
DetailContainer,
HeaderTop,
HeaderDetails,
BottomDetails,
StatusText,
PIBIcon,
UserIcon,
UserIconContainer,
} from "./ItemDetailsHeaderCard.styled";
import { ItemDetailsHeaderContainer } from "./ItemDetailsHeaderCard.styled";
import { ReactComponent as Category } from "../../../assets/images/svg/category.svg";
import { ReactComponent as PIB } from "../../../assets/images/svg/pib.svg";
import { ReactComponent as MessageColor } from "../../../assets/images/svg/mailColor.svg";
import selectedTheme from "../../../themes";
import { useHistory } from "react-router-dom";
import { useSelector } from "react-redux";
import { selectLatestChats } from "../../../store/selectors/chatSelectors";
import { selectUserId } from "../../../store/selectors/loginSelectors";
import StatisticDetails from "./StatisticDetails/StatisticDetails";
import PIBDetail from "./OfferDetail/PIB/PIBDetail";
import CategoryDetail from "./OfferDetail/Category/CategoryDetail";

const ItemDetailsHeaderCard = (props) => {
const history = useHistory();
const chats = useSelector(selectLatestChats);
const offer = props.offer;
const userId = useSelector(selectUserId);
if (!props.offer) {
return <div>Loading...</div>;
}
let percentOfSucceededExchanges;
if (offer?.companyData?.statistics?.exchanges?.succeeded === 0) {
percentOfSucceededExchanges = 0;
} else {
percentOfSucceededExchanges = Math.ceil(
(offer?.companyData?.statistics?.exchanges?.total /
offer?.companyData?.statistics?.exchanges?.succeeded) *
100
);
}

const handleGoProfile = () => {
history.push(`/profile/${offer?.offer?.userId}`);
};
@@ -72,26 +53,8 @@ const ItemDetailsHeaderCard = (props) => {
<OfferTitle isMyProfile={props.isMyProfile} onClick={handleGoProfile}>
{offer?.companyData?.company?.name}
</OfferTitle>
<DetailContainer>
<PIBIcon color={selectedTheme.iconStrokeColor} component="span">
<PIB />
</PIBIcon>
<DetailText isMyProfile={props.isMyProfile}>
PIB - {offer?.companyData?.company?.PIB}
</DetailText>
</DetailContainer>
<DetailContainer shouldHideResponsive>
<DetailIcon
color={selectedTheme.iconStrokeColor}
component="span"
size="22px"
>
<Category width={"22px"} />
</DetailIcon>
<DetailText isMyProfile={props.isMyProfile}>
{offer?.companyData?.company?.contacts?.location}
</DetailText>
</DetailContainer>
<PIBDetail offer={props.offer} isMyProfile={props.isMyProfile}/>
<CategoryDetail offer={props.offer} isMyProfile={props.isMyProfile}/>
</OfferDetails>
{props.isMyProfile ? (
<UserIconContainer onClick={handleGoProfile}>
@@ -103,23 +66,8 @@ const ItemDetailsHeaderCard = (props) => {
</MessageIcon>
)}
</HeaderTop>
<HeaderDetails>
<BottomDetails>
<StatusText>
<b>{offer?.companyData?.statistics?.publishes?.count}</b> objava
</StatusText>
<StatusText>
<b>{offer?.companyData?.statistics?.views?.count}</b> ukupnih
pregleda
</StatusText>
<StatusText>
<b>{percentOfSucceededExchanges}</b> % uspesnih trampi
</StatusText>
<StatusText>
<b>{percentOfSucceededExchanges}</b> % korektna komunikacija
</StatusText>
</BottomDetails>
</HeaderDetails>
<StatisticDetails offer={offer} />
</ItemDetailsHeaderContainer>
);
};

+ 2
- 69
src/components/ItemDetails/ItemDetailsHeaderCard/ItemDetailsHeaderCard.styled.js Прегледај датотеку

@@ -1,9 +1,8 @@
import { Box, Grid, Typography } from "@mui/material";
import { Box, Typography } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../themes";
import { IconButton } from "../../Buttons/IconButton/IconButton";
import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton";
import { Icon } from "../../Icon/Icon";
import { ReactComponent as User} from "../../../assets/images/svg/user.svg";


@@ -22,42 +21,12 @@ export const ItemDetailsHeaderContainer = styled(Box)`
max-width: 2000px;
position: relative;
`;
export const DetailContainer = styled(Box)`
display: flex;
flex-direction: row;
align-items: center;
gap: 7px;
font-family: "Open Sans";
color: ${selectedTheme.primaryText};
line-height: 16px;
margin-bottom: 7px;
font-size: 12px;
@media (max-width: 600px) {
${(props) => props.shouldHideResponsive && `display: none;`}
}
`;
export const HeaderTop = styled(Box)`
display: flex;
flex-direction: row;
padding: 18px;
gap: 18px;
`;
export const HeaderDetails = styled(Box)`
background-color: ${selectedTheme.primaryIconBackgroundColor};
`;
export const BottomDetails = styled(Box)`
max-width: fit-content;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 1fr);
grid-column-gap: 12px;
grid-row-gap: 12px;
padding: 18px;
@media (max-width: 600px) {
display: flex;
flex-direction: column;
}
`;
export const OfferImage = styled.img`
border-radius: 50%;
width: 144px;
@@ -116,13 +85,6 @@ export const OfferDetails = styled(Box)`
`;

export const StatusText = styled(Grid)`
font-family: "Open Sans";
color: ${selectedTheme.primaryText};
@media (max-width: 600px) {
font-size: 12px;
}
`;
export const OfferCategory = styled(Box)`
font-family: "Open Sans";
color: ${selectedTheme.primaryText};
@@ -173,24 +135,7 @@ export const Line = styled(Box)`
width: 0;
margin: auto 0;
`;
export const DetailIcon = styled(Icon)`
display: flex;
align-items: center;
& svg {
width: 22px;
position: relative;
}
`;
export const DetailText = styled(Typography)`
font-family: "Open Sans";
color: ${props => props.isMyProfile ? "white" : selectedTheme.primaryText};
line-height: 16px;
font-size: 16px;
position: relative;
@media (max-width: 600px) {
font-size: 14px;
}
`;

export const CheckButton = styled(PrimaryButton)`
width: 180px;
height: 48px;
@@ -224,18 +169,6 @@ export const MessageIcon = styled(IconButton)`
}
}
`;
export const PIBIcon = styled(DetailIcon)`
position: relative;
top: 1px;
& span svg {
width: 22px;
height: 22px;
@media (max-width: 600px) {
width: 14px;
height: 14px;
}
}
`;
export const UserIconContainer = styled(MessageIcon)`
background-color: ${selectedTheme.primaryIconBackgroundColor};
`

+ 30
- 0
src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/Category/CategoryDetail.js Прегледај датотеку

@@ -0,0 +1,30 @@
import React from "react";
import PropTypes from "prop-types";
import {
DetailContainer,
DetailIcon,
DetailText,
} from "./CategoryDetail.styled";
import selectedTheme from "../../../../../themes";
import { ReactComponent as Category } from "../../../../../assets/images/svg/category.svg";

const CategoryDetail = (props) => {
const offer = props.offer;
return (
<DetailContainer shouldHideResponsive>
<DetailIcon color={selectedTheme.iconStrokeColor} size="22px">
<Category width={"22px"} />
</DetailIcon>
<DetailText isMyProfile={props.isMyProfile}>
{offer?.companyData?.company?.contacts?.location}
</DetailText>
</DetailContainer>
);
};

CategoryDetail.propTypes = {
offer: PropTypes.any,
isMyProfile: PropTypes.bool,
};

export default CategoryDetail;

+ 37
- 0
src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/Category/CategoryDetail.styled.js Прегледај датотеку

@@ -0,0 +1,37 @@
import { Box, Typography } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../../../themes";
import { Icon } from "../../../../Icon/Icon";

export const DetailContainer = styled(Box)`
display: flex;
flex-direction: row;
align-items: center;
gap: 7px;
font-family: "Open Sans";
color: ${selectedTheme.primaryText};
line-height: 16px;
margin-bottom: 7px;
font-size: 12px;
@media (max-width: 600px) {
${(props) => props.shouldHideResponsive && `display: none;`}
}
`;
export const DetailIcon = styled(Icon)`
display: flex;
align-items: center;
& svg {
width: 22px;
position: relative;
}
`;
export const DetailText = styled(Typography)`
font-family: "Open Sans";
color: ${props => props.isMyProfile ? "white" : selectedTheme.primaryText};
line-height: 16px;
font-size: 16px;
position: relative;
@media (max-width: 600px) {
font-size: 14px;
}
`;

+ 30
- 0
src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/PIB/PIBDetail.js Прегледај датотеку

@@ -0,0 +1,30 @@
import React from "react";
import PropTypes from "prop-types";
import { DetailContainer, DetailText, PIBIcon } from "./PIBDetail.styled";
import selectedTheme from "../../../../../themes";
import { useTranslation } from "react-i18next";
import { ReactComponent as PIB } from "../../../../../assets/images/svg/pib.svg";


const PIBDetail = (props) => {
const { t } = useTranslation();
const offer = props.offer;
return (
<DetailContainer>
<PIBIcon color={selectedTheme.iconStrokeColor} component="span">
<PIB />
</PIBIcon>
<DetailText isMyProfile={props.isMyProfile}>
{`${t("itemDetailsCard.PIB")}${offer?.companyData?.company?.PIB}`}
</DetailText>
</DetailContainer>
);
};

PIBDetail.propTypes = {
isMyProfile: PropTypes.bool,
offer: PropTypes.any,
icon: PropTypes.node,
};

export default PIBDetail;

+ 49
- 0
src/components/ItemDetails/ItemDetailsHeaderCard/OfferDetail/PIB/PIBDetail.styled.js Прегледај датотеку

@@ -0,0 +1,49 @@
import { Box, Typography } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../../../themes";
import { Icon } from "../../../../Icon/Icon";

export const DetailContainer = styled(Box)`
display: flex;
flex-direction: row;
align-items: center;
gap: 7px;
font-family: "Open Sans";
color: ${selectedTheme.primaryText};
line-height: 16px;
margin-bottom: 7px;
font-size: 12px;
@media (max-width: 600px) {
${(props) => props.shouldHideResponsive && `display: none;`}
}
`;
export const DetailIcon = styled(Icon)`
display: flex;
align-items: center;
& svg {
width: 22px;
position: relative;
}
`;
export const DetailText = styled(Typography)`
font-family: "Open Sans";
color: ${props => props.isMyProfile ? "white" : selectedTheme.primaryText};
line-height: 16px;
font-size: 16px;
position: relative;
@media (max-width: 600px) {
font-size: 14px;
}
`;
export const PIBIcon = styled(DetailIcon)`
position: relative;
top: 1px;
& span svg {
width: 22px;
height: 22px;
@media (max-width: 600px) {
width: 14px;
height: 14px;
}
}
`;

+ 60
- 0
src/components/ItemDetails/ItemDetailsHeaderCard/StatisticDetails/StatisticDetails.js Прегледај датотеку

@@ -0,0 +1,60 @@
import React, { useMemo } from "react";
import PropTypes from "prop-types";
import {
BottomDetails,
HeaderDetails,
StatusText,
StatusValue,
} from "./StatisticDetails.styled";
import { useTranslation } from "react-i18next";

const StatisticDetails = (props) => {
const { t } = useTranslation();
const offer = props.offer;
const percentOfSucceededExchanges = useMemo(() => {
if (offer?.companyData?.statistics?.exchanges?.succeeded === 0) {
return 0 + "%";
} else {
return (
Math.ceil(
(offer?.companyData?.statistics?.exchanges?.total /
offer?.companyData?.statistics?.exchanges?.succeeded) *
100
) + "%"
);
}
});

return (
<HeaderDetails>
<BottomDetails>
<StatusText>
<StatusValue>
{offer?.companyData?.statistics?.publishes?.count}
</StatusValue>
{t("itemDetailsCard.offers")}
</StatusText>
<StatusText>
<StatusValue>
{offer?.companyData?.statistics?.views?.count}
</StatusValue>
{t("itemDetailsCard.totalViews")}
</StatusText>
<StatusText>
<StatusValue>{percentOfSucceededExchanges}</StatusValue>
{t("itemDetailsCard.successfulExchanges")}
</StatusText>
<StatusText>
<StatusValue>{percentOfSucceededExchanges}</StatusValue>
{t("itemDetailsCard.correctCommunications")}
</StatusText>
</BottomDetails>
</HeaderDetails>
);
};

StatisticDetails.propTypes = {
offer: PropTypes.any,
};

export default StatisticDetails;

+ 30
- 0
src/components/ItemDetails/ItemDetailsHeaderCard/StatisticDetails/StatisticDetails.styled.js Прегледај датотеку

@@ -0,0 +1,30 @@
import { Box, Grid } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../../themes";

export const HeaderDetails = styled(Box)`
background-color: ${selectedTheme.primaryIconBackgroundColor};
`;
export const BottomDetails = styled(Box)`
max-width: fit-content;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 1fr);
grid-column-gap: 12px;
grid-row-gap: 12px;
padding: 18px;
@media (max-width: 600px) {
display: flex;
flex-direction: column;
}
`;
export const StatusText = styled(Grid)`
font-family: "Open Sans";
color: ${selectedTheme.primaryText};
@media (max-width: 600px) {
font-size: 12px;
}
`;
export const StatusValue = styled.b`
font-weight: bold;
`

+ 0
- 59
src/components/ItemDetails/MockupdataDetails.js Прегледај датотеку

@@ -1,59 +0,0 @@
import React from 'react'
import {ReactComponent as DummyImage1 } from "../../assets/images/svg/dummyImages/offer-1.svg"
import {ReactComponent as DummyAuthorImage1} from "../../assets/images/svg/dummyImages/DummyAuthorImage1.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 const Author = {
id: 0,
image: <DummyAuthorImage1 />,
title: "Women's Beauty House",
pib: 123456789,
location: "Nis, Serbia",
numberOfOffers: 9,
numberOfViews: 1200,
successSwapsProcent: "75%",
goodCommunicationProcent: "90%",
}

export const Offer = {
id: 0,
title: "Vino",
category: "Hrana i pice",
subcategory:"Farbe",
status:"novo",
quantity:150,
numberOfViews:45,
description: "Vinarija Aleksić osnovana je u Vranju 2006. godine, otvorivši time put oživljavanju vinogradarstva na jugu Srbije.Vinarija Aleksić osnovana je u Vranju 2006. godine, otvorivši time put oživljavanju vinogradarstva na jugu Srbije.Vinarija Aleksić osnovana je u Vranju 2006. godine, otvorivši time put oživljavanju vinogradarstva na jugu Srbije.Vinarija Aleksić osnovana je u Vranju 2006. godine, otvorivši time put oživljavanju vinogradarstva na jugu Srbije.",
images: [
{
id:0,
image: <DummyImage1 />
},
{
id:1,
image: <DummyImage1 />
},
{
id:2,
image: <DummyImage1 />
},
{
id:3,
image: <DummyImage1 />
},
{
id:4,
image: <DummyImage1 />
},
],
package: packageEnum.package,
postDate: "12.04.2022",
}

+ 0
- 26
src/components/Loader/BlockSectionLoader.js Прегледај датотеку

@@ -1,26 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';

const BlockSectionLoader = ({ children, isLoading, fullHeight, noShadow }) => (
<div
className={`c-loader__wrapper c-loader__wrapper--block ${
fullHeight ? 'c-loader__wrapper--full-height' : ''
} ${noShadow ? 'c-loader__wrapper--no-shadow' : ''}`}
>
{children}
{isLoading && (
<div className="c-loader">
<div className="c-loader__icon" />
</div>
)}
</div>
);

BlockSectionLoader.propTypes = {
children: PropTypes.node,
isLoading: PropTypes.bool,
fullHeight: PropTypes.bool,
noShadow: PropTypes.bool,
};

export default BlockSectionLoader;

+ 0
- 13
src/components/Loader/FullPageLoader.js Прегледај датотеку

@@ -1,13 +0,0 @@
import React from "react";
import { ReactComponent as Logo } from "../../assets/images/svg/big-logo-vertical.svg";
import { FullPageLoaderContainer } from "./FullPageLoader.styled";

const FullPageLoader = () => {
return (
<FullPageLoaderContainer>
<Logo />
</FullPageLoaderContainer>
);
};

export default FullPageLoader;

+ 0
- 10
src/components/Loader/FullPageLoader.styled.js Прегледај датотеку

@@ -1,10 +0,0 @@

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

export const FullPageLoaderContainer = styled(Container)`
height: 100%;
width: 100vw;
padding-top: 250px;
text-align: center;
`

+ 0
- 20
src/components/Loader/SectionLoader.js Прегледај датотеку

@@ -1,20 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';

const SectionLoader = ({ children, isLoading }) => (
<div className="c-loader__wrapper">
{children}
{isLoading && (
<div className="c-loader">
<div className="c-loader__icon" />
</div>
)}
</div>
);

SectionLoader.propTypes = {
children: PropTypes.node,
isLoading: PropTypes.bool,
};

export default SectionLoader;

+ 19
- 0
src/components/Login/Description/LoginDescription.js Прегледај датотеку

@@ -0,0 +1,19 @@
import React from "react";
import PropTypes from "prop-types";
import { LoginDescription as Description } from "./LoginDescription.styled";
import { useTranslation } from "react-i18next";

const LoginDescription = () => {
const { t } = useTranslation();
return (
<Description component="h1" variant="h6">
{t("login.welcomeText")}
</Description>
);
};

LoginDescription.propTypes = {
children: PropTypes.node,
};

export default LoginDescription;

+ 18
- 0
src/components/Login/Description/LoginDescription.styled.js Прегледај датотеку

@@ -0,0 +1,18 @@
import { Typography } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../themes";

export const LoginDescription = styled(Typography)`
font-family: "Open Sans";
margin-top: 9px;
width: 221px;
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 22px;
display: flex;
align-items: center;
text-align: center;
color: ${selectedTheme.primaryGrayText};
margin-bottom: 20px;
`;

+ 26
- 0
src/components/Login/ErrorMessage/ErrorMessage.js Прегледај датотеку

@@ -0,0 +1,26 @@
import React from "react";
import PropTypes from "prop-types";
import { useSelector } from "react-redux";
import { selectLoginError } from "../../../store/selectors/loginSelectors";
import { ErrorText } from "./ErrorMessage.styled";

const ErrorMessage = (props) => {
const formik = props.formik;
const error = useSelector(selectLoginError);
return (
<>
{formik.errors.password && formik.touched.password && (
<ErrorText>{formik.errors.password}</ErrorText>
)}
{error.length > 0 && !formik.errors.password && (
<ErrorText>{error}</ErrorText>
)}
</>
);
};

ErrorMessage.propTypes = {
formik: PropTypes.any,
};

export default ErrorMessage;

+ 11
- 0
src/components/Login/ErrorMessage/ErrorMessage.styled.js Прегледај датотеку

@@ -0,0 +1,11 @@
import { Typography } from "@mui/material";
import styled from "styled-components";

export const ErrorText = styled(Typography)`
color: red;
font-family: "Open Sans";
position: relative;
top: -12px;
height: 20px;
font-size: 14px;
`;

+ 30
- 0
src/components/Login/Fields/Email/EmailField.js Прегледај датотеку

@@ -0,0 +1,30 @@
import React from "react";
import PropTypes from "prop-types";
import { selectLoginError } from "../../../../store/selectors/loginSelectors";
import { useSelector } from "react-redux";
import { TextField } from "../../../TextFields/TextField/TextField";
import { useTranslation } from "react-i18next";

const EmailField = (props) => {
const { t } = useTranslation();
const error = useSelector(selectLoginError);
const formik = props.formik;
return (
<TextField
name="email"
placeholder={t("common.labelEmail")}
value={formik.values.email}
onChange={formik.handleChange}
error={(formik.touched.email && formik.errors.email) || error.length > 0}
helperText={formik.touched.email && formik.errors.email}
autoFocus
fullWidth
/>
);
};

EmailField.propTypes = {
formik: PropTypes.any,
};

export default EmailField;

+ 0
- 0
src/components/Login/Fields/Email/EmailField.styled.js Прегледај датотеку


+ 50
- 0
src/components/Login/Fields/Password/PasswordField.js Прегледај датотеку

@@ -0,0 +1,50 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import IconButton from "../../../IconButton/IconButton";
import { ReactComponent as VisibilityOn } from "../../../../assets/images/svg/eye-striked.svg";
import { ReactComponent as VisibilityOff } from "../../../../assets/images/svg/eye.svg";
import { useSelector } from "react-redux";
import { selectLoginError } from "../../../../store/selectors/loginSelectors";
import { TextField } from "../../../TextFields/TextField/TextField";
import { useTranslation } from "react-i18next";

const PasswordField = (props) => {
const formik = props.formik;
const [showPassword, setShowPassword] = useState(false);
const handleClickShowPassword = () => setShowPassword(!showPassword);
const handleMouseDownPassword = () => setShowPassword(!showPassword);
const error = useSelector(selectLoginError);
const {t} = useTranslation();

return (
<TextField
name="password"
placeholder={t("common.labelPassword")}
margin="normal"
type={showPassword ? "text" : "password"}
value={formik.values.password}
onChange={formik.handleChange}
error={
(formik.touched.password && formik.errors.password) || error.length > 0
}
helperText={formik.touched.password && formik.errors.password}
fullWidth
InputProps={{
endAdornment: (
<IconButton
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
>
{showPassword ? <VisibilityOn /> : <VisibilityOff />}
</IconButton>
),
}}
/>
);
};

PasswordField.propTypes = {
formik: PropTypes.any,
};

export default PasswordField;

+ 0
- 0
src/components/Login/Fields/Password/PasswordField.styled.js Прегледај датотеку


+ 34
- 0
src/components/Login/ForgotPasswordLink/ForgotPasswordLink.js Прегледај датотеку

@@ -0,0 +1,34 @@
import React from "react";
import PropTypes from "prop-types";
import { useSelector } from "react-redux";
import { selectLoginError } from "../../../store/selectors/loginSelectors";
import { FORGOT_PASSWORD_PAGE } from "../../../constants/pages";
import Link from "../../Link/Link";
import { useTranslation } from "react-i18next";
import { NavLink } from "react-router-dom";

const ForgotPasswordLink = () => {
const error = useSelector(selectLoginError);
const { t } = useTranslation();
return (
<Link
to={FORGOT_PASSWORD_PAGE}
textsize="12px"
component={NavLink}
underline="hover"
align="right"
style={{
marginTop: error.length > 0 ? "0" : "18px",
marginBottom: "18px",
}}
>
{t("login.forgotYourPassword")}
</Link>
);
};

ForgotPasswordLink.propTypes = {
children: PropTypes.node,
};

export default ForgotPasswordLink;

+ 0
- 0
src/components/Login/ForgotPasswordLink/ForgotPasswordLink.styled.js Прегледај датотеку


+ 98
- 0
src/components/Login/Login.js Прегледај датотеку

@@ -0,0 +1,98 @@
import React, { useEffect } from "react";
import PropTypes from "prop-types";
import { useFormik } from "formik";
import { useDispatch, useSelector } from "react-redux";
import { useHistory } from "react-router-dom";
import {
clearLoginErrors,
fetchLogin,
} from "../../store/actions/login/loginActions";
import { selectLoginError } from "../../store/selectors/loginSelectors";
import { HOME_PAGE } from "../../constants/pages";
import { ReactComponent as Logo } from "../../assets/images/svg/logo-vertical.svg";
import { LoginPageContainer, LoginFormContainer } from "./Login.styled";
import loginValidation from "../../validations/loginValidation";
import loginInitialValues from "../../initialValues/loginInitialValues";
import LoginTitle from "./Title/LoginTitle";
import LoginDescription from "./Description/LoginDescription";
import EmailField from "./Fields/Email/EmailField";
import PasswordField from "./Fields/Password/PasswordField";
import ErrorMessage from "./ErrorMessage/ErrorMessage";
import ForgotPasswordLink from "./ForgotPasswordLink/ForgotPasswordLink";
import LoginButton from "./LoginButton/LoginButton";
import RegisterLink from "./RegisterLink/RegisterLink";

const Login = () => {
const dispatch = useDispatch();
const error = useSelector(selectLoginError);
const history = useHistory();

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

const handleApiResponseSuccess = () => {
history.push({
pathname: HOME_PAGE,
state: {
from: history.location.pathname,
},
});
};

const handleSubmit = (values) => {
const { email, password } = values;
dispatch(clearLoginErrors());
dispatch(
fetchLogin({
email,
password,
handleApiResponseSuccess,
})
);
console.log(values);
};

const formik = useFormik({
initialValues: loginInitialValues,
validationSchema: loginValidation,
onSubmit: handleSubmit,
validateOnBlur: true,
enableReinitialize: true,
});

useEffect(() => {
if (error) {
if (formik.errors.email || formik.errors.password) {
dispatch(clearLoginErrors());
}
}
}, [formik.errors.email, formik.errors.password]);

return (
<LoginPageContainer>
<Logo />
<LoginTitle />
<LoginDescription />
<LoginFormContainer component="form" onSubmit={formik.handleSubmit}>
<EmailField formik={formik} />
<PasswordField formik={formik} />
<ErrorMessage formik={formik} />
<ForgotPasswordLink />
<LoginButton formik={formik} />
<RegisterLink />
</LoginFormContainer>
</LoginPageContainer>
);
};

Login.propTypes = {
history: PropTypes.shape({
replace: PropTypes.func,
push: PropTypes.func,
location: PropTypes.shape({
pathname: PropTypes.string,
}),
}),
};
export default Login;

+ 19
- 0
src/components/Login/Login.styled.js Прегледај датотеку

@@ -0,0 +1,19 @@
import { Box, Container } from "@mui/material";
import styled from "styled-components";

export const LoginPageContainer = styled(Container)`
margin-top: 150px;
display: flex;
flex-direction: column;
align-items: center;
@media (max-height: 900px) {
margin-top: 110px;
}
@media (max-height: 800px) {
margin-top: 70px;
}
`;
export const LoginFormContainer = styled(Box)`
width: 335px;
height: 216px;
`;

+ 32
- 0
src/components/Login/LoginButton/LoginButton.js Прегледај датотеку

@@ -0,0 +1,32 @@
import React from "react";
import PropTypes from "prop-types";
import selectedTheme from "../../../themes";
import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton";
import { useTranslation } from "react-i18next";

const LoginButton = (props) => {
const { t } = useTranslation();
const formik = props.formik;
return (
<PrimaryButton
type="submit"
variant="contained"
height="48px"
fullWidth
buttoncolor={selectedTheme.primaryPurple}
onClick={formik.handleSubmit}
textcolor="white"
disabled={
formik.values.email.length === 0 || formik.values.password.length === 0
}
>
{t("login.logIn")}
</PrimaryButton>
);
};

LoginButton.propTypes = {
formik: PropTypes.any,
};

export default LoginButton;

+ 0
- 0
src/components/Login/LoginButton/LoginButton.styled.js Прегледај датотеку


+ 27
- 0
src/components/Login/RegisterLink/RegisterLink.js Прегледај датотеку

@@ -0,0 +1,27 @@
import React from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
import { RegisterAltText, RegisterTextContainer } from "./RegisterLink.styled";
import Link from "../../Link/Link";
import { NavLink } from "react-router-dom";

const RegisterLink = () => {
const { t } = useTranslation();
return (
<RegisterTextContainer>
<RegisterAltText>
{t("login.dontHaveAccount").padEnd(2, " ")}
</RegisterAltText>

<Link to="/register" component={NavLink} underline="hover" align="center">
{t("login.signUp")}
</Link>
</RegisterTextContainer>
);
};

RegisterLink.propTypes = {
children: PropTypes.any,
};

export default RegisterLink;

+ 20
- 0
src/components/Login/RegisterLink/RegisterLink.styled.js Прегледај датотеку

@@ -0,0 +1,20 @@
import { Box, Typography } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../themes";

export const RegisterAltText = styled(Typography)`
font-family: "Poppins";
color: ${selectedTheme.primaryText};
font-size: 14px;
padding-right: 6px;
line-height: 14px;
`;
export const RegisterTextContainer = styled(Box)`
display: flex;
flex-direction: row;
margin-top: 36px;
justify-content: center;
@media (max-width: 600px) {
padding-bottom: 36px;
}
`;

+ 19
- 0
src/components/Login/Title/LoginTitle.js Прегледај датотеку

@@ -0,0 +1,19 @@
import React from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
import { LoginTitle as Title } from "./LoginTitle.styled";

const LoginTitle = () => {
const { t } = useTranslation();
return (
<Title component="h1" variant="h5">
{t("login.logInTitle")}
</Title>
);
};

LoginTitle.propTypes = {
children: PropTypes.node,
};

export default LoginTitle;

+ 17
- 0
src/components/Login/Title/LoginTitle.styled.js Прегледај датотеку

@@ -0,0 +1,17 @@
import { Typography } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../themes";

export const LoginTitle = styled(Typography)`
font-family: "Open Sans";
width: 328px;
height: 33px;
text-align: center;
flex: 1;
font-style: normal;
font-weight: 400;
font-size: 24px;
line-height: 33px;
color: ${selectedTheme.primaryPurple};
margin-top: 36px;
`;

+ 0
- 57
src/components/MUI/DialogComponent.js Прегледај датотеку

@@ -1,57 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Dialog,
DialogContent,
DialogTitle,
DialogActions,
Button,
useMediaQuery,
useTheme,
} from '@mui/material';

const DialogComponent = ({
title,
content,
onClose,
open,
maxWidth,
fullWidth,
responsive,
}) => {
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down('md'));

const handleClose = () => {
onClose();
};

return (
<Dialog
maxWidth={maxWidth}
fullWidth={fullWidth}
fullScreen={responsive && fullScreen}
onClose={handleClose}
open={open}
>
<DialogTitle>{title}</DialogTitle>
{content && <DialogContent>{content}</DialogContent>}
<DialogActions>
<Button onClick={handleClose}>OK</Button>
<Button onClick={handleClose}>Cancel</Button>
</DialogActions>
</Dialog>
);
};

DialogComponent.propTypes = {
title: PropTypes.string,
open: PropTypes.bool.isRequired,
content: PropTypes.any,
onClose: PropTypes.func.isRequired,
maxWidth: PropTypes.any,
fullWidth: PropTypes.bool,
responsive: PropTypes.bool,
};

export default DialogComponent;

+ 21
- 21
src/components/MUI/DrawerComponent.js Прегледај датотеку

@@ -1,28 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Drawer } from '@mui/material';
import React from "react";
import PropTypes from "prop-types";
import { Drawer } from "@mui/material";

const DrawerComponent = ({ open, toggleOpen, content, anchor = 'right' }) => (
<Drawer
sx={{
minWidth: 250,
'& .MuiDrawer-paper': {
minWidth: 250,
},
}}
anchor={anchor}
open={open}
onClose={toggleOpen}
>
{content ? content : null}
</Drawer>
const DrawerComponent = ({ open, toggleOpen, content, anchor = "right" }) => (
<Drawer
sx={{
minWidth: 250,
"& .MuiDrawer-paper": {
minWidth: 250,
},
}}
anchor={anchor}
open={open}
onClose={toggleOpen}
>
{content ? content : null}
</Drawer>
);

DrawerComponent.propTypes = {
open: PropTypes.bool,
toggleOpen: PropTypes.func,
content: PropTypes.any,
anchor: PropTypes.oneOf(['top', 'right', 'left', 'bottom']),
open: PropTypes.bool,
toggleOpen: PropTypes.func,
content: PropTypes.any,
anchor: PropTypes.oneOf(["top", "right", "left", "bottom"]),
};

export default DrawerComponent;

+ 0
- 0
src/components/MUI/ErrorMessageComponent.js Прегледај датотеку


Неке датотеке нису приказане због велике количине промена

Loading…
Откажи
Сачувај