Explorar el Código

Cleaned code

feature/498
Djordje Mitrovic hace 3 años
padre
commit
b183e38a81
Se han modificado 79 ficheros con 1191 adiciones y 1023 borrados
  1. 7
    6
      src/AppRoutes.js
  2. 4
    1
      src/components/Buttons/ArrowButton/ArrowButton.styled.js
  3. 1
    1
      src/components/Cards/ChatCard/ChatCommands/ChatCommands.styled.js
  4. 0
    2
      src/components/Cards/FilterCard/FilterCard.js
  5. 4
    5
      src/components/Cards/FilterCard/Skeleton/SkeletonChooserHeader/SkeletonChooserHeader.js
  6. 2
    3
      src/components/Cards/FilterCard/Skeleton/SkeletonChooserTitle/SkeletonChooserTitle.js
  7. 11
    12
      src/components/Cards/FilterCard/Skeleton/SkeletonFilterCard.js
  8. 1
    2
      src/components/Cards/FilterCard/Skeleton/SkeletonSection/SkeletonSection.js
  9. 4
    5
      src/components/Cards/FilterCard/Skeleton/SkeletonSection/SkeletonSectionOption/SkeletonSectionOption.js
  10. 0
    1
      src/components/Cards/OfferCard/SkeletonOfferCard/SkeletonOfferCard.js
  11. 25
    0
      src/components/Cards/ProfileCard/EditProfile/AppLinkField/AppLinkField.js
  12. 0
    0
      src/components/Cards/ProfileCard/EditProfile/AppLinkField/AppLinkField.styled.js
  13. 167
    0
      src/components/Cards/ProfileCard/EditProfile/EditProfile.js
  14. 4
    35
      src/components/Cards/ProfileCard/EditProfile/EditProfile.styled.js
  15. 27
    0
      src/components/Cards/ProfileCard/EditProfile/FirmNameField/FirmNameField.js
  16. 0
    0
      src/components/Cards/ProfileCard/EditProfile/FirmNameField/FirmNameField.styled.js
  17. 28
    0
      src/components/Cards/ProfileCard/EditProfile/FormikErrorMessage/FormikErrorMessage.js
  18. 11
    0
      src/components/Cards/ProfileCard/EditProfile/FormikErrorMessage/FormikErrorMessage.styled.js
  19. 42
    0
      src/components/Cards/ProfileCard/EditProfile/LocationField/LocationField.js
  20. 25
    0
      src/components/Cards/ProfileCard/EditProfile/LocationField/LocationField.styled.js
  21. 29
    0
      src/components/Cards/ProfileCard/EditProfile/PIBField/PIBField.js
  22. 0
    0
      src/components/Cards/ProfileCard/EditProfile/PIBField/PIBField.styled.js
  23. 39
    0
      src/components/Cards/ProfileCard/EditProfile/PhoneField/PhoneField.js
  24. 0
    0
      src/components/Cards/ProfileCard/EditProfile/PhoneField/PhoneField.styled.js
  25. 29
    0
      src/components/Cards/ProfileCard/EditProfile/WebsiteField/WebsiteField.js
  26. 0
    0
      src/components/Cards/ProfileCard/EditProfile/WebsiteField/WebsiteField.styled.js
  27. 7
    8
      src/components/Cards/ProfileCard/ProfileCard.js
  28. 3
    3
      src/components/Cards/ProfileCard/ProfileCard.styled.js
  29. 0
    0
      src/components/Cards/ProfileCard/ProfileContact/ProfileContact.js
  30. 4
    4
      src/components/Cards/ProfileCard/ProfileContact/ProfileContact.styled.js
  31. 2
    2
      src/components/Cards/ProfileCard/ProfileMainInfo/ProfileMainInfo.js
  32. 2
    2
      src/components/Cards/ProfileCard/ProfileMainInfo/ProfileMainInfo.styled.js
  33. 0
    0
      src/components/Cards/ProfileCard/ProfileStats/ProfileStats.js
  34. 1
    1
      src/components/Cards/ProfileCard/ProfileStats/ProfileStats.styled.js
  35. 0
    0
      src/components/Cards/ProfileCard/SkeletonProfileCard/SkeletonProfileCard.js
  36. 1
    1
      src/components/Cards/ProfileCard/SkeletonProfileCard/SkeletonProfileCard.styled.js
  37. 1
    1
      src/components/ChatColumn/ChatColumn.js
  38. 1
    1
      src/components/CreateReview/CreateReview.styled.js
  39. 32
    201
      src/components/Header/Header.js
  40. 1
    15
      src/components/Header/Header.styled.js
  41. 32
    0
      src/components/Header/LoginButton/LoginButton.js
  42. 9
    0
      src/components/Header/LoginButton/LoginButton.styled.js
  43. 43
    12
      src/components/Header/MyMessagesButton/MyMessagesButton.js
  44. 42
    10
      src/components/Header/MySwapsButton/MySwapsButton.js
  45. 32
    0
      src/components/Header/RegisterButton/RegisterButton.js
  46. 11
    0
      src/components/Header/RegisterButton/RegisterButton.styled.js
  47. 44
    12
      src/components/Header/UserButton/UserButton.js
  48. 0
    32
      src/components/IconButton/IconButton.js
  49. 1
    1
      src/components/ImagePicker/ImagePicker.js
  50. 5
    15
      src/components/Login/Fields/Password/PasswordField.js
  51. 0
    2
      src/components/MarketPlace/Header/Header.js
  52. 4
    5
      src/components/MarketPlace/Header/SkeletonHeader/SkeletonHeader.js
  53. 0
    3
      src/components/MarketPlace/MarketPlace.js
  54. 0
    2
      src/components/MarketPlace/Offers/Offers.js
  55. 1
    1
      src/components/Profile/Profile.js
  56. 26
    0
      src/components/Profile/ProfileOffers/HeaderTitle/HeaderTitle.js
  57. 32
    0
      src/components/Profile/ProfileOffers/HeaderTitle/HeaderTitle.styled.js
  58. 21
    120
      src/components/Profile/ProfileOffers/ProfileOffers.js
  59. 1
    85
      src/components/Profile/ProfileOffers/ProfileOffers.styled.js
  60. 24
    16
      src/components/Profile/ProfileOffers/ProfileOffersHeaderSkeleton/ProfileOffersHeaderSkeleton.js
  61. 1
    1
      src/components/Profile/ProfileOffers/ProfileOffersHeaderSkeleton/ProfileOffersHeaderSkeleton.styled.js
  62. 53
    0
      src/components/Profile/ProfileOffers/SearchBar/SearchBar.js
  63. 29
    0
      src/components/Profile/ProfileOffers/SearchBar/SearchBar.styled.js
  64. 76
    0
      src/components/Profile/ProfileOffers/SelectSortField/SelectSortField.js
  65. 38
    0
      src/components/Profile/ProfileOffers/SelectSortField/SelectSortField.styled.js
  66. 0
    274
      src/components/ProfileCard/EditProfile/EditProfile.js
  67. 21
    0
      src/components/Router/AuthRoute.js
  68. 14
    11
      src/i18n/resources/rs.js
  69. 9
    0
      src/initialValues/editProfileInitialValues.js
  70. 17
    16
      src/layouts/ProfileLayout/ProfileLayout.js
  71. 45
    32
      src/layouts/ProfileLayout/ProfileLayout.styled.js
  72. 0
    7
      src/pages/HomePage/HomePageMUI.js
  73. 0
    7
      src/pages/MyOffers/MyOffers.js
  74. 6
    2
      src/pages/ProfilePage/ProfilePage.js
  75. 11
    18
      src/store/reducers/login/loginReducer.js
  76. 1
    1
      src/store/saga/loginSaga.js
  77. 2
    2
      src/store/saga/registerSaga.js
  78. 4
    24
      src/store/selectors/loginSelectors.js
  79. 21
    0
      src/util/helpers/routeHelpers.js

+ 7
- 6
src/AppRoutes.js Ver fichero

@@ -41,20 +41,21 @@ import ChatPage from "./pages/Chat/Chat";
import MyOffers from "./pages/MyOffers/MyOffers";
// import PricesPage from "./pages/Prices/PricesPage";
import AboutPage from "./pages/About/AboutPage";
import AuthRoute from "./components/Router/AuthRoute";
// import PrivacyPolicyPage from "./pages/PrivacyPolicy/PrivacyPolicyPage";

const AppRoutes = () => {
return (
<Switch>
<Route exact path={BASE_PAGE} component={HomePage} />
<Route exact path={LOGIN_PAGE} component={LoginPage} />
<AuthRoute exact path={LOGIN_PAGE} component={LoginPage} />
<Route path={NOT_FOUND_PAGE} component={NotFoundPage} />
<Route path={REGISTER_SUCCESSFUL_PAGE} component={RegisterSuccessful} />
<Route path={REGISTER_PAGE} component={Register} />
<Route path={ERROR_PAGE} component={ErrorPage} />
<Route path={FORGOT_PASSWORD_MAIL_SENT} component={MailSent} />
<Route path={FORGOT_PASSWORD_PAGE} component={ForgotPasswordPage} />
<Route path={RESET_PASSWORD_PAGE} component={ResetPasswordPage} />
<AuthRoute path={REGISTER_SUCCESSFUL_PAGE} component={RegisterSuccessful} />
<AuthRoute path={REGISTER_PAGE} component={Register} />
<AuthRoute path={FORGOT_PASSWORD_MAIL_SENT} component={MailSent} />
<AuthRoute path={FORGOT_PASSWORD_PAGE} component={ForgotPasswordPage} />
<AuthRoute path={RESET_PASSWORD_PAGE} component={ResetPasswordPage} />
<Route path={CREATE_OFFER_PAGE} component={CreateOffer} />
<Route path={ITEM_DETAILS_PAGE} component={ItemDetailsPage} />
<Route path={PROFILE_PAGE} component={ProfilePage} />

+ 4
- 1
src/components/Buttons/ArrowButton/ArrowButton.styled.js Ver fichero

@@ -1,7 +1,7 @@
import IconButton from "../../IconButton/IconButton";
import { ReactComponent as DownArrow } from "../../../assets/images/svg/arrow-down.svg";
import styled from "styled-components";
import selectedTheme from "../../../themes";
import { IconButton } from "../IconButton/IconButton";

export const ArrowIcon = styled(DownArrow)`
${(props) =>
@@ -11,6 +11,9 @@ export const ArrowIcon = styled(DownArrow)`
`}
width: 18px;
height: 18px;
position: relative;
top: 1px;
left: 1px;
& path {
${(props) =>
props.disabled &&

+ 1
- 1
src/components/Cards/ChatCard/ChatCommands/ChatCommands.styled.js Ver fichero

@@ -2,8 +2,8 @@ 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 { IconButton } from "../../../Buttons/IconButton/IconButton";
import { PrimaryButton } from "../../../Buttons/PrimaryButton/PrimaryButton";
import IconButton from "../../../IconButton/IconButton";
import PopoverComponent from "../../../Popovers/PopoverComponent";



+ 0
- 2
src/components/Cards/FilterCard/FilterCard.js Ver fichero

@@ -27,7 +27,6 @@ const FilterCard = (props) => {
skeleton={props.skeleton}
>
<SkeletonFilterCard
animationStage={props.animationStage}
skeleton={props.skeleton}
/>
{/* Header title for my offers */}
@@ -75,7 +74,6 @@ FilterCard.propTypes = {
closeResponsive: PropTypes.func,
myOffers: PropTypes.bool,
skeleton: PropTypes.bool,
animationStage: PropTypes.number,
filtersOpened: PropTypes.bool,
toggleFilters: PropTypes.func,
};

+ 4
- 5
src/components/Cards/FilterCard/Skeleton/SkeletonChooserHeader/SkeletonChooserHeader.js Ver fichero

@@ -8,21 +8,20 @@ import {
SkeletonChooserContainer,
} from "./SkeletonChooserHeader.styled";

const SkeletonChooserHeader = (props) => {
const SkeletonChooserHeader = () => {
return (
<SkeletonChooserContainer>
<LeftContainer>
<CircleOne animationStage={props.animationStage}/>
<Line animationStage={props.animationStage}/>
<CircleOne/>
<Line/>
</LeftContainer>
<CircleSecond animationStage={props.animationStage}/>
<CircleSecond/>
</SkeletonChooserContainer>
);
};

SkeletonChooserHeader.propTypes = {
children: PropTypes.node,
animationStage: PropTypes.any,
};

export default SkeletonChooserHeader;

+ 2
- 3
src/components/Cards/FilterCard/Skeleton/SkeletonChooserTitle/SkeletonChooserTitle.js Ver fichero

@@ -4,8 +4,8 @@ import { SkeletonChooserTitleContainer, SkeletonChooserTitleLine } from './Skele

const SkeletonChooserTitle = (props) => {
return (
<SkeletonChooserTitleContainer center={props.center} animationStage={props.animationStage} >
<SkeletonChooserTitleLine center={props.center} animationStage={props.animationStage}/>
<SkeletonChooserTitleContainer center={props.center} >
<SkeletonChooserTitleLine center={props.center} />
</SkeletonChooserTitleContainer>
)
}
@@ -13,7 +13,6 @@ const SkeletonChooserTitle = (props) => {
SkeletonChooserTitle.propTypes = {
children: PropTypes.any,
center: PropTypes.bool,
animationStage: PropTypes.number,
}

export default SkeletonChooserTitle

+ 11
- 12
src/components/Cards/FilterCard/Skeleton/SkeletonFilterCard.js Ver fichero

@@ -13,26 +13,25 @@ import SkeletonSection from "./SkeletonSection/SkeletonSection";
const SkeletonFilterCard = (props) => {

return (
<SkeletonFilterCardContainer animationStage={props.animationStage} skeleton={props.skeleton}>
<SkeletonFilterCardContainer skeleton={props.skeleton}>
<SkeletonHeader>
<SkeletonHeaderLineOne animationStage={props.animationStage} />
<SkeletonHeaderLineSecond animationStage={props.animationStage} />
<SkeletonHeaderLineOne />
<SkeletonHeaderLineSecond />
</SkeletonHeader>
<SkeletonChooserHeader animationStage={props.animationStage}/>
<SkeletonChooserTitle animationStage={props.animationStage} />
<SkeletonSection numberOfOptions={14} animationStage={props.animationStage} />
<SkeletonChooserHeader animationStage={props.animationStage} />
<SkeletonChooserHeader animationStage={props.animationStage} />
<SkeletonChooserTitle animationStage={props.animationStage} />
<SkeletonSection numberOfOptions={3} animationStage={props.animationStage} />
<SkeletonChooserTitle center animationStage={props.animationStage} />
<SkeletonChooserHeader/>
<SkeletonChooserTitle />
<SkeletonSection numberOfOptions={14} />
<SkeletonChooserHeader />
<SkeletonChooserHeader />
<SkeletonChooserTitle />
<SkeletonSection numberOfOptions={3} />
<SkeletonChooserTitle center />
</SkeletonFilterCardContainer>
);
};

SkeletonFilterCard.propTypes = {
children: PropTypes.any,
animationStage: PropTypes.number,
skeleton: PropTypes.bool,
};


+ 1
- 2
src/components/Cards/FilterCard/Skeleton/SkeletonSection/SkeletonSection.js Ver fichero

@@ -10,7 +10,7 @@ const SkeletonSection = (props) => {
return (
<SkeletonSectionContainer>
{arrayForMapping.map((item, index) => (
<SkeletonSectionOption key={index} animationStage={props.animationStage} />
<SkeletonSectionOption key={index} />
))}
</SkeletonSectionContainer>
);
@@ -19,7 +19,6 @@ const SkeletonSection = (props) => {
SkeletonSection.propTypes = {
children: PropTypes.node,
numberOfOptions: PropTypes.number,
animationStage: PropTypes.number,
};

export default SkeletonSection;

+ 4
- 5
src/components/Cards/FilterCard/Skeleton/SkeletonSection/SkeletonSectionOption/SkeletonSectionOption.js Ver fichero

@@ -2,21 +2,20 @@ import React from 'react'
import PropTypes from 'prop-types'
import { Circle, EndLine, Line, OptionLeftContainer, SkeletonSectionOptionContainer } from './SkeletonSectionOption.styled'

const SkeletonSectionOption = (props) => {
const SkeletonSectionOption = () => {
return (
<SkeletonSectionOptionContainer>
<OptionLeftContainer>
<Circle animationStage={props.animationStage} />
<Line animationStage={props.animationStage} />
<Circle/>
<Line/>
</OptionLeftContainer>
<EndLine animationStage={props.animationStage} />
<EndLine/>
</SkeletonSectionOptionContainer>
)
}

SkeletonSectionOption.propTypes = {
children: PropTypes.any,
animationStage: PropTypes.number,
}

export default SkeletonSectionOption

+ 0
- 1
src/components/Cards/OfferCard/SkeletonOfferCard/SkeletonOfferCard.js Ver fichero

@@ -57,7 +57,6 @@ const SkeletonOfferCard = (props) => {
SkeletonOfferCard.propTypes = {
children: PropTypes.node,
skeleton: PropTypes.bool,
animationStage: PropTypes.number,
};

export default SkeletonOfferCard;

+ 25
- 0
src/components/Cards/ProfileCard/EditProfile/AppLinkField/AppLinkField.js Ver fichero

@@ -0,0 +1,25 @@
import React from "react";
import PropTypes from "prop-types";
import { InputField, InputFieldLabel } from "../EditProfile.styled";
import { useTranslation } from "react-i18next";

const AppLinkField = (props) => {
const { t } = useTranslation();
return (
<>
<InputFieldLabel leftText={t("editProfile.applink").toUpperCase()} />
<InputField
name="firmApplink"
values={props.formik.values.firmApplink}
margin="normal"
fullWidth
/>
</>
);
};

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

export default AppLinkField;

+ 0
- 0
src/components/Cards/ProfileCard/EditProfile/AppLinkField/AppLinkField.styled.js Ver fichero


+ 167
- 0
src/components/Cards/ProfileCard/EditProfile/EditProfile.js Ver fichero

@@ -0,0 +1,167 @@
import React, { useState, useEffect } from "react";
import PropTypes from "prop-types";
import BackdropComponent from "../../../MUI/BackdropComponent";
import {
EditProfileContainer,
ProfileImageContainer,
BackButton,
CloseButton,
SaveButton,
ProfileHeader,
BasicInfo,
DetailsInfo,
ButtonsContainer,
ProfileImagePicker,
} from "./EditProfile.styled";
import selectedTheme from "../../../../themes";
import { useFormik } from "formik";
import { ReactComponent as ArrowBack } from "../../../../assets/images/svg/arrow-back.svg";
import { ReactComponent as CloseIcon } from "../../../../assets/images/svg/close-modal.svg";
import { useTranslation } from "react-i18next";
import {
editMineProfile,
fetchMineProfile,
} from "../../../../store/actions/profile/profileActions";
import { useDispatch, useSelector } from "react-redux";
import { selectUserId } from "../../../../store/selectors/loginSelectors";
import editProfileValidation from "../../../../validations/editProfileValidation";
import useIsMobile from "../../../../hooks/useIsMobile";
import { useMemo } from "react";
import editProfileInitialValues from "../../../../initialValues/editProfileInitialValues";
import FirmNameField from "./FirmNameField/FirmNameField";
import PIBField from "./PIBField/PIBField";
import LocationField from "./LocationField/LocationField";
import WebsiteField from "./WebsiteField/WebsiteField";
import AppLinkField from "./AppLinkField/AppLinkField";
import PhoneField from "./PhoneField/PhoneField";
import FormikErrorMessage from "./FormikErrorMessage/FormikErrorMessage";

const EditProfile = (props) => {
const [profileImage, setProfileImage] = useState(props.profile.image);
const [showBasic, setShowBasic] = useState(true);
const [showDetails, setShowDetails] = useState(true);
const { t } = useTranslation();
const dispatch = useDispatch();
const { isMobile } = useIsMobile();
const userId = useSelector(selectUserId);

useEffect(() => {
setShowDetails(!isMobile);
}, [isMobile]);

const handleApiResponseSuccess = () => {
dispatch(fetchMineProfile(userId));
props.reFetchProfile();
};

const handleSubmit = (values) => {
dispatch(editMineProfile({ ...values, handleApiResponseSuccess }));
props.closeModalHandler();
};
const initialValues = useMemo(
() => editProfileInitialValues(props?.profile),
[props?.profile]
);

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

const closeEditModalHandler = () => {
props.closeModalHandler();
};

const showDetailsHandler = () => {
setShowDetails(!showDetails);
setShowBasic(!showBasic);
};

const setImage = (image) => {
setProfileImage(image);
};

return (
<>
<BackdropComponent
handleClose={closeEditModalHandler}
isLoading
position="fixed"
/>
<EditProfileContainer component="form" onSubmit={formik.handleSubmit}>
{!showBasic && (
<BackButton onClick={showDetailsHandler}>
<ArrowBack />
</BackButton>
)}
<ProfileImageContainer>
<ProfileImagePicker
image={profileImage}
setImage={setImage}
></ProfileImagePicker>
<ProfileHeader>{props.profile.company.name}</ProfileHeader>
</ProfileImageContainer>
<CloseButton onClick={closeEditModalHandler}>
<CloseIcon />
</CloseButton>
{showBasic && (
<BasicInfo>
<FirmNameField formik={formik} />
<PIBField formik={formik} />
<LocationField formik={formik} />
</BasicInfo>
)}
{showDetails && (
<DetailsInfo>
<WebsiteField formik={formik} />
<AppLinkField formik={formik} />
<PhoneField formik={formik} />
</DetailsInfo>
)}

<FormikErrorMessage formik={formik} />

<ButtonsContainer>
{isMobile && (
<>
<SaveButton
height="44px"
width="155px"
buttoncolor={selectedTheme.colors.primaryPurple}
textcolor={selectedTheme.colors.primaryPurple}
onClick={showDetailsHandler}
>
{showDetails
? t("editProfile.showBasic")
: t("editProfile.showDetails")}
</SaveButton>
</>
)}
<SaveButton
type="submit"
variant="contained"
height={isMobile ? "44px" : "48px"}
width={isMobile ? "155px" : "335px"}
buttoncolor={selectedTheme.colors.primaryPurple}
textcolor="white"
>
{t("common.save")}
</SaveButton>
</ButtonsContainer>
</EditProfileContainer>
</>
);
};

EditProfile.propTypes = {
children: PropTypes.node,
profile: PropTypes.any,
closeModalHandler: PropTypes.func,
setImage: PropTypes.func,
reFetchProfile: PropTypes.func,
};

export default EditProfile;

src/components/ProfileCard/EditProfile/EditProfile.styled.js → src/components/Cards/ProfileCard/EditProfile/EditProfile.styled.js Ver fichero

@@ -1,9 +1,9 @@
import styled from "styled-components";
import { Box, TextField, Typography } from "@mui/material";
import ImagePicker from "../../ImagePicker/ImagePicker";
import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton";
import { Label } from "../../CheckBox/Label";
import selectedTheme from "../../../themes";
import ImagePicker from "../../../ImagePicker/ImagePicker";
import { PrimaryButton } from "../../../Buttons/PrimaryButton/PrimaryButton";
import { Label } from "../../../CheckBox/Label";
import selectedTheme from "../../../../themes";

export const EditProfileContainer = styled(Box)`
background-color: #fff;
@@ -120,29 +120,6 @@ export const InputField = styled(TextField)`
}
`;

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

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

export const SaveButton = styled(PrimaryButton)`
font-size: 12px;
letter-spacing: 1.5px;
@@ -158,14 +135,6 @@ export const ButtonsContainer = styled(Box)`
}
`;

export const ErrorMessage = styled(Typography)`
color: red;
font-family: ${selectedTheme.fonts.textFont};
position: relative;
top: 20px;
font-size: 14px;
`;

export const BasicInfo = styled(Box)``;

export const DetailsInfo = styled(Box)``;

+ 27
- 0
src/components/Cards/ProfileCard/EditProfile/FirmNameField/FirmNameField.js Ver fichero

@@ -0,0 +1,27 @@
import React from "react";
import PropTypes from "prop-types";
import { InputField, InputFieldLabel } from "../EditProfile.styled";
import { useTranslation } from "react-i18next";

const FirmNameField = (props) => {
const { t } = useTranslation();
return (
<>
<InputFieldLabel leftText={t("common.labelFirm").toUpperCase()} />
<InputField
name="firmName"
value={props.formik.values.firmName}
onChange={props.formik.handleChange}
error={props.formik.touched.firmName && props.formik.errors.firmName}
margin="normal"
fullWidth
/>
</>
);
};

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

export default FirmNameField;

+ 0
- 0
src/components/Cards/ProfileCard/EditProfile/FirmNameField/FirmNameField.styled.js Ver fichero


+ 28
- 0
src/components/Cards/ProfileCard/EditProfile/FormikErrorMessage/FormikErrorMessage.js Ver fichero

@@ -0,0 +1,28 @@
import React from "react";
import PropTypes from "prop-types";
import { ErrorMessage } from "./FormikErrorMessage.styled";

const FormikErrorMessage = (props) => {
return (
<>
{props.formik.errors.firmName && props.formik.touched.firmName ? (
<ErrorMessage>{props.formik.errors.firmName}</ErrorMessage>
) : props.formik.errors.firmPIB && props.formik.touched.firmPIB ? (
<ErrorMessage>{props.formik.errors.firmPIB}</ErrorMessage>
) : props.formik.errors.firmLocation &&
props.formik.touched.firmLocation ? (
<ErrorMessage>{props.formik.errors.firmLocation}</ErrorMessage>
) : props.formik.errors.firmPhone && props.formik.touched.firmPhone ? (
<ErrorMessage>{props.formik.errors.firmPhone}</ErrorMessage>
) : (
<></>
)}
</>
);
};

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

export default FormikErrorMessage;

+ 11
- 0
src/components/Cards/ProfileCard/EditProfile/FormikErrorMessage/FormikErrorMessage.styled.js Ver fichero

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

export const ErrorMessage = styled(Typography)`
color: red;
font-family: ${selectedTheme.fonts.textFont};
position: relative;
top: 20px;
font-size: 14px;
`;

+ 42
- 0
src/components/Cards/ProfileCard/EditProfile/LocationField/LocationField.js Ver fichero

@@ -0,0 +1,42 @@
import React from "react";
import PropTypes from "prop-types";
import AutoSuggestTextField from "../../../../TextFields/AutoSuggestTextField/AutoSuggestTextField";
import { InputFieldLabelLocation } from "./LocationField.styled";
import { useDispatch, useSelector } from "react-redux";
import { selectLocations } from "../../../../../store/selectors/locationsSelectors";
import { useEffect } from "react";
import { fetchLocations } from "../../../../../store/actions/locations/locationsActions";
import { useTranslation } from "react-i18next";

const LocationField = (props) => {
const { t } = useTranslation();
const locations = useSelector(selectLocations);
const dispatch = useDispatch();
useEffect(() => {
if (locations?.length === 0) {
dispatch(fetchLocations());
}
}, [locations]);

return (
<>
<InputFieldLabelLocation
leftText={t("common.labelLocation").toUpperCase()}
/>
<AutoSuggestTextField
editLocation
data={locations.map((item) => ({ name: item.city }))}
value={props.formik.values.firmLocation}
onChange={(event, { newValue }) =>
props.formik.setFieldValue("firmLocation", newValue)
}
/>
</>
);
};

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

export default LocationField;

+ 25
- 0
src/components/Cards/ProfileCard/EditProfile/LocationField/LocationField.styled.js Ver fichero

@@ -0,0 +1,25 @@
import styled from "styled-components";
import { Label } from "../../../../CheckBox/Label";

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

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

+ 29
- 0
src/components/Cards/ProfileCard/EditProfile/PIBField/PIBField.js Ver fichero

@@ -0,0 +1,29 @@
import React from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
import { InputField, InputFieldLabel } from "../EditProfile.styled";

const PIBField = (props) => {
const { t } = useTranslation();
return (
<>
<InputFieldLabel leftText={t("common.labelPIB")} />
<InputField
name="firmPIB"
type="number"
value={props.formik.values.firmPIB}
onChange={props.formik.handleChange}
error={props.formik.touched.firmPIB && props.formik.errors.firmPIB}
margin="normal"
fullWidth
disabled
/>
</>
);
};

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

export default PIBField;

+ 0
- 0
src/components/Cards/ProfileCard/EditProfile/PIBField/PIBField.styled.js Ver fichero


+ 39
- 0
src/components/Cards/ProfileCard/EditProfile/PhoneField/PhoneField.js Ver fichero

@@ -0,0 +1,39 @@
import React from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
import { InputField, InputFieldLabel } from "../EditProfile.styled";

const PhoneField = (props) => {
const { t } = useTranslation();
return (
<>
<InputFieldLabel leftText={t("editProfile.phoneNumber").toUpperCase()} />
<InputField
type="number"
name="firmPhone"
value={props.formik.values.firmPhone}
onChange={(event) => {
props.formik.setFieldValue("firmPhone", event.target.value);
}}
error={props.formik.touched.firmPhone && props.formik.errors.firmPhone}
margin="normal"
fullWidth
onInput={(e) => {
e.target.value =
e.target.value[0] === "0" && e.target.value.length > 1
? "0" +
String(
Math.max(0, parseInt(e.target.value)).toString().slice(0, 14)
)
: Math.max(0, parseInt(e.target.value)).toString().slice(0, 14);
}}
/>
</>
);
};

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

export default PhoneField;

+ 0
- 0
src/components/Cards/ProfileCard/EditProfile/PhoneField/PhoneField.styled.js Ver fichero


+ 29
- 0
src/components/Cards/ProfileCard/EditProfile/WebsiteField/WebsiteField.js Ver fichero

@@ -0,0 +1,29 @@
import React from "react";
import PropTypes from "prop-types";
import { InputField, InputFieldLabel } from "../EditProfile.styled";
import { useTranslation } from "react-i18next";

const WebsiteField = (props) => {
const { t } = useTranslation();
return (
<>
<InputFieldLabel
leftText={t("editProfile.website").toUpperCase()}
labelWebsite
/>
<InputField
name="firmWebsite"
value={props.formik.values.firmWebsite}
onChange={props.formik.handleChange}
margin="normal"
fullWidth
/>
</>
);
};

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

export default WebsiteField;

+ 0
- 0
src/components/Cards/ProfileCard/EditProfile/WebsiteField/WebsiteField.styled.js Ver fichero


src/components/ProfileCard/ProfileCard.js → src/components/Cards/ProfileCard/ProfileCard.js Ver fichero

@@ -13,23 +13,23 @@ import {
} from "./ProfileCard.styled";
import PersonOutlineIcon from "@mui/icons-material/PersonOutline";
import { useRouteMatch } from "react-router-dom";
import { fetchProfile } from "../../store/actions/profile/profileActions";
import { fetchProfile } from "../../../store/actions/profile/profileActions";
import { useDispatch, useSelector } from "react-redux";
import { useEffect } from "react";
import { selectProfile } from "../../store/selectors/profileSelectors";
import { selectUserId } from "../../store/selectors/loginSelectors";
import { selectProfile } from "../../../store/selectors/profileSelectors";
import { selectUserId } from "../../../store/selectors/loginSelectors";
import { useState } from "react";
import { fetchProfileOffers } from "../../store/actions/offers/offersActions";
import { fetchProfileOffers } from "../../../store/actions/offers/offersActions";
import EditProfile from "./EditProfile/EditProfile";
import ProfileMainInfo from "./ProfileMainInfo/ProfileMainInfo";
import ProfileContact from "./ProfileContact/ProfileContact";
import ProfileStats from "./ProfileStats/ProfileStats";
import { useTranslation } from "react-i18next";
import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors";
import { PROFILE_SCOPE } from "../../store/actions/profile/profileActionConstants";
import { selectIsLoadingByActionType } from "../../../store/selectors/loadingSelectors";
import { PROFILE_SCOPE } from "../../../store/actions/profile/profileActionConstants";
import SkeletonProfileCard from "./SkeletonProfileCard/SkeletonProfileCard";
import { useMemo } from "react";
import companyData from "../../notFoundData/companyData";
import companyData from "../../../notFoundData/companyData";

const ProfileCard = () => {
const [isMyProfile, setIsMyProfile] = useState(false);
@@ -51,7 +51,6 @@ const ProfileCard = () => {
return companyData;
}, [profileFromRedux]);

console.log("profile", profile);

useEffect(() => {
if (idProfile?.length > 0) {

src/components/ProfileCard/ProfileCard.styled.js → src/components/Cards/ProfileCard/ProfileCard.styled.js Ver fichero

@@ -1,10 +1,10 @@
import styled from "styled-components";
import { Card, Typography, Grid, Box } from "@mui/material";
import selectedTheme from "../../themes";
import { ReactComponent as Edit } from "../../assets/images/svg/edit.svg";
import selectedTheme from "../../../themes";
import { ReactComponent as Edit } from "../../../assets/images/svg/edit.svg";
// import { ReactComponent as Pocket } from "../../assets/images/svg/pocket.svg";
// import { ReactComponent as Globe } from "../../assets/images/svg/globe.svg";
import { ReactComponent as Mail } from "../../assets/images/svg/mail.svg";
import { ReactComponent as Mail } from "../../../assets/images/svg/mail.svg";
// import { ReactComponent as Location } from "../../assets/images/svg/location.svg";
// import { PRIMARY_PURPLE_COLOR, PRIMARY_YELLOW_COLOR } from '../../constants/stylesConstants';


src/components/ProfileCard/ProfileContact/ProfileContact.js → src/components/Cards/ProfileCard/ProfileContact/ProfileContact.js Ver fichero


src/components/ProfileCard/ProfileContact/ProfileContact.styled.js → src/components/Cards/ProfileCard/ProfileContact/ProfileContact.styled.js Ver fichero

@@ -1,9 +1,9 @@
import styled from "styled-components";
import { Grid, Typography } from "@mui/material";
import { ReactComponent as Location } from "../../../assets/images/svg/location.svg";
import { ReactComponent as Mail } from "../../../assets/images/svg/mail.svg";
import { ReactComponent as Globe } from "../../../assets/images/svg/globe.svg";
import selectedTheme from "../../../themes";
import { ReactComponent as Location } from "../../../../assets/images/svg/location.svg";
import { ReactComponent as Mail } from "../../../../assets/images/svg/mail.svg";
import { ReactComponent as Globe } from "../../../../assets/images/svg/globe.svg";
import selectedTheme from "../../../../themes";

export const ProfileContactContainer = styled(Grid)`
padding-top: 2rem;

src/components/ProfileCard/ProfileMainInfo/ProfileMainInfo.js → src/components/Cards/ProfileCard/ProfileMainInfo/ProfileMainInfo.js Ver fichero

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

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

src/components/ProfileCard/ProfileMainInfo/ProfileMainInfo.styled.js → src/components/Cards/ProfileCard/ProfileMainInfo/ProfileMainInfo.styled.js Ver fichero

@@ -1,7 +1,7 @@
import styled from "styled-components";
import { Grid, Typography } from "@mui/material";
import selectedTheme from "../../../themes";
import { ReactComponent as Pocket } from "../../../assets/images/svg/pocket.svg";
import selectedTheme from "../../../../themes";
import { ReactComponent as Pocket } from "../../../../assets/images/svg/pocket.svg";

export const ProfileMainInfoContainer = styled(Grid)`
display: flex;

src/components/ProfileCard/ProfileStats/ProfileStats.js → src/components/Cards/ProfileCard/ProfileStats/ProfileStats.js Ver fichero


src/components/ProfileCard/ProfileStats/ProfileStats.styled.js → src/components/Cards/ProfileCard/ProfileStats/ProfileStats.styled.js Ver fichero

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

export const ProfileStatsContainer = styled(Grid)`
display: flex;

src/components/ProfileCard/SkeletonProfileCard/SkeletonProfileCard.js → src/components/Cards/ProfileCard/SkeletonProfileCard/SkeletonProfileCard.js Ver fichero


src/components/ProfileCard/SkeletonProfileCard/SkeletonProfileCard.styled.js → src/components/Cards/ProfileCard/SkeletonProfileCard/SkeletonProfileCard.styled.js Ver fichero

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

const skeletonAnimation = keyframes`
0% {

+ 1
- 1
src/components/ChatColumn/ChatColumn.js Ver fichero

@@ -14,7 +14,7 @@ import { ReactComponent as Down } from "../../assets/images/svg/down-arrow.svg";
import { IconStyled } from "../Icon/Icon.styled";
import { Grid } from "@mui/material";
import MailOutlineIcon from "@mui/icons-material/MailOutline";
import { HeaderTitle } from "../ProfileCard/ProfileCard.styled";
import { HeaderTitle } from "../Cards/ProfileCard/ProfileCard.styled";
import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
import { selectLatestChats } from "../../store/selectors/chatSelectors";

+ 1
- 1
src/components/CreateReview/CreateReview.styled.js Ver fichero

@@ -1,10 +1,10 @@
import { Box, Typography } from "@mui/material";
import styled from "styled-components";
import { PrimaryButton } from "../Buttons/PrimaryButton/PrimaryButton";
import IconButton from "../IconButton/IconButton";
import { ReactComponent as Close } from "../../assets/images/svg/close-modal.svg";
import { ReactComponent as ArrowBack } from "../../assets/images/svg/arrow-back.svg";
import selectedTheme from "../../themes";
import { IconButton } from "../Buttons/IconButton/IconButton";

export const CreateReviewContainer = styled(Box)`
background-color: #fff;

+ 32
- 201
src/components/Header/Header.js Ver fichero

@@ -1,50 +1,25 @@
import React, { useState, useEffect, useRef } from "react";
import {
AuthButtonsContainer,
// EndIcon,
// FilterContainer,
// FilterIcon,
HeaderContainer,
LoginButton,
LogoContainer,
RegisterButton,
// SearchIcon,
// SearchInput,
// SearchInputMobile,
ToolsButtonsContainer,
ToolsContainer,
} from "./Header.styled";
import PropTypes from "prop-types";
import { AppBar, Toolbar, useMediaQuery } from "@mui/material";
import { useTheme } from "@mui/system";
import PopoverComponent from "../Popovers/PopoverComponent";
import { MyPosts } from "../Popovers/MyPosts/MyPosts";
import { MyMessages } from "../Popovers/MyMessages/MyMessages";
import { MyProfile } from "../Popovers/MyProfile/MyProfile";
import { ReactComponent as LogoHorizontal } from "../../assets/images/svg/logo-horizontal.svg";
import selectedTheme from "../../themes";
import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
import { selectUserId } from "../../store/selectors/loginSelectors";
import { selectProfileName } from "../../store/selectors/profileSelectors";
import { useHistory, useRouteMatch } from "react-router-dom";
import {
ABOUT_PAGE,
BASE_PAGE,
FORGOT_PASSWORD_MAIL_SENT,
FORGOT_PASSWORD_PAGE,
HOME_PAGE,
LOGIN_PAGE,
REGISTER_PAGE,
REGISTER_SUCCESSFUL_PAGE,
RESET_PASSWORD_PAGE,
} from "../../constants/pages";
import { ABOUT_PAGE, BASE_PAGE, HOME_PAGE } from "../../constants/pages";
import { fetchMineProfile } from "../../store/actions/profile/profileActions";
import CreateOffer from "../Cards/CreateOfferCard/CreateOffer";
import useSearch from "../../hooks/useOffers/useSearch";
import { routeMatches } from "../../util/helpers/routeHelpers";
import { isAuthRoute, routeMatches } from "../../util/helpers/routeHelpers";
import AboutHeader from "./AboutHeader/AboutHeader";
// import { useCallback } from "react";
import SearchInput from "./SearchInput/SearchInput";
import DrawerContainer from "./DrawerContainer/DrawerContainer";
import OpenDrawerButton from "./OpenDrawerButton/OpenDrawerButton";
@@ -52,11 +27,11 @@ import AddOfferButton from "./AddOfferButton/AddOfferButton";
import MySwapsButton from "./MySwapsButton/MySwapsButton";
import MyMessagesButton from "./MyMessagesButton/MyMessagesButton";
import UserButton from "./UserButton/UserButton";
import LoginButton from "./LoginButton/LoginButton";
import RegisterButton from "./RegisterButton/RegisterButton";

const Header = () => {
const setShowSearchBar = useState(true)[1];
const [showCreateOfferModal, setShowCreateOfferModal] = useState(false);
const { t } = useTranslation();
const theme = useTheme();
const searchRef = useRef(null);
const matches = useMediaQuery(theme.breakpoints.down("md"));
@@ -65,85 +40,37 @@ const Header = () => {
const dispatch = useDispatch();
const name = useSelector(selectProfileName);
const history = useHistory();
const routeMatch = useRouteMatch();
const drawerRef = useRef(null);
const [shouldShow, setShouldShow] = useState(true);
const routeMatch = useRouteMatch();

// Dont show header on auth routes(login, register, etc.)
useEffect(() => {
if (isAuthRoute()) setShouldShow(false);
else setShouldShow(true);
}, [routeMatch]);

// Fetch mine profile on loading home page
useEffect(() => {
if (user && user?.length > 0) {
dispatch(fetchMineProfile());
}
}, []);
useEffect(() => {
setUserPopoverOpen(false);
setUserAnchorEl(null);
return () => {
setUserPopoverOpen(false);
setUserAnchorEl(null);
};
}, []);

// Sets value into search input based on query string
useEffect(() => {
if (searchRef.current) {
searchRef.current.value = search.searchString ?? "";
}
}, [search.searchString]);
useEffect(() => {
if (history.location.pathname !== "/home") {
setShowSearchBar(false);
} else {
setShowSearchBar(true);
}
}, [history.location.pathname]);

const closeCreateOfferModal = () => {
setShowCreateOfferModal(false);
};
}, [search.searchString, searchRef.current]);

// Removes scroll when modal is open
if (showCreateOfferModal) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "auto";
}

const [postsPopoverOpen, setPostsPopoverOpen] = useState(false);
const [postsAnchorEl, setPostsAnchorEl] = useState(null);

const [msgPopoverOpen, setMsgPopoverOpen] = useState(false);
const [msgAnchorEl, setMsgAnchorEl] = useState(null);

const [userPopoverOpen, setUserPopoverOpen] = useState(false);
const [userAnchorEl, setUserAnchorEl] = useState(null);

const [shouldShow, setShouldShow] = useState(true);

useEffect(() => {
let shouldShowHeader = true;
if (
routeMatches(LOGIN_PAGE) ||
routeMatches(REGISTER_PAGE) ||
routeMatches(REGISTER_SUCCESSFUL_PAGE) ||
routeMatches(FORGOT_PASSWORD_PAGE) ||
routeMatches(FORGOT_PASSWORD_MAIL_SENT) ||
routeMatches(RESET_PASSWORD_PAGE)
) {
shouldShowHeader = false;
}
setShouldShow(shouldShowHeader);
}, [location.pathname, user, routeMatch]);

useEffect(() => {
setUserPopoverOpen(false);
setMsgPopoverOpen(false);
setPostsPopoverOpen(false);
}, [location.pathname]);
const handleNavigateLogin = () => {
setShouldShow(false);
history.push(LOGIN_PAGE);
};
const handleNavigateRegister = () => {
setShouldShow(false);
history.push(REGISTER_PAGE);
};

// Handling search when user is on marketplace and when he is not
const handleSearch = (value) => {
if (!routeMatches(HOME_PAGE) && !routeMatches(BASE_PAGE)) {
const newQueryString = new URLSearchParams({ search: value });
@@ -155,10 +82,8 @@ const Header = () => {
search.searchOffers(value);
}
};
// const toggleFilters = () => {
// setOpenFilters((prevState) => !prevState);
// };

// When user clicks logo, he sends specific state so filters can be removed
const handleLogoClick = () => {
history.push({
pathname: HOME_PAGE,
@@ -167,34 +92,12 @@ const Header = () => {
},
});
};

const handleAddOfferClick = () => {
setUserPopoverOpen(false);
setUserAnchorEl(null);
setShowCreateOfferModal(true);
};
const openPostsPopover = (event) => {
setPostsPopoverOpen(true);
setPostsAnchorEl(event.currentTarget);
};
const openMesgPopover = (event) => {
setMsgPopoverOpen(true);
setMsgAnchorEl(event.currentTarget);
};
const openUserPopover = (event) => {
setUserPopoverOpen(true);
setUserAnchorEl(event.currentTarget);
};
const closePostsPopover = () => {
setPostsPopoverOpen(false);
setPostsAnchorEl(null);
};
const closeMsgPopover = () => {
setMsgPopoverOpen(false);
setMsgAnchorEl(null);
};
const closeUserPopover = () => {
setUserPopoverOpen(false);
setUserAnchorEl(null);
const closeCreateOfferModal = () => {
setShowCreateOfferModal(false);
};

return (
@@ -210,11 +113,6 @@ const Header = () => {
<LogoHorizontal />
</LogoContainer>

<DrawerContainer
ref={drawerRef}
showCreateOfferModal={setShowCreateOfferModal}
/>

<SearchInput ref={searchRef} handleSearch={handleSearch} />
{routeMatches(ABOUT_PAGE) && <AboutHeader />}

@@ -232,11 +130,11 @@ const Header = () => {
{!routeMatches(ABOUT_PAGE) && (
<>
<AddOfferButton onClick={handleAddOfferClick} />
<MySwapsButton onClick={openPostsPopover} />
<MyMessagesButton onClick={openMesgPopover} />
<MySwapsButton />
<MyMessagesButton />
</>
)}
<UserButton onClick={openUserPopover} name={name}/>
<UserButton name={name} />
</React.Fragment>
)}
</ToolsButtonsContainer>
@@ -248,87 +146,20 @@ const Header = () => {
/>
) : (
<React.Fragment>
<LoginButton
type="submit"
variant="contained"
fullWidth
buttoncolor={selectedTheme.colors.primaryPurple}
textcolor="white"
onClick={handleNavigateLogin}
>
{t("login.headerTitle")}
</LoginButton>
<RegisterButton
type="submit"
variant="contained"
fullWidth
buttoncolor={selectedTheme.colors.primaryYellow}
textcolor={selectedTheme.colors.yellowButtonTextColor}
onClick={handleNavigateRegister}
>
{t("register.headerTitle")}
</RegisterButton>
<LoginButton />
<RegisterButton />
</React.Fragment>
)}
</AuthButtonsContainer>
)}
</ToolsContainer>
</Toolbar>
{user && (
<React.Fragment>
<PopoverComponent
anchorEl={postsAnchorEl}
open={postsPopoverOpen}
onClose={() => {
setPostsPopoverOpen(false);
setPostsAnchorEl(null);
}}
content={<MyPosts closePopover={closePostsPopover} />}
/>
<PopoverComponent
anchorEl={msgAnchorEl}
open={msgPopoverOpen}
onClose={() => {
setMsgPopoverOpen(false);
setMsgAnchorEl(null);
}}
content={<MyMessages closePopover={closeMsgPopover} />}
/>
<PopoverComponent
anchorEl={userAnchorEl}
open={userPopoverOpen}
onClose={() => {
setUserPopoverOpen(false);
setUserAnchorEl(null);
}}
content={<MyProfile closePopover={closeUserPopover} />}
/>
</React.Fragment>
)}
</AppBar>
{/* <SearchInputMobile
fullWidth
shouldShow={showSearchBar}
ref={searchMobileRef}
placeholder={t("header.searchOffers")}
InputProps={{
endAdornment: (
<EndIcon size="36px">
<SearchIcon
onClick={() => handleSearch(searchMobileRef.current.value)}
/>
</EndIcon>
),
}}
italicPlaceholder
onFocus={handleFocusSearch}
onBlur={handleBlurSearch}
/> */}
{/* <FilterCard
responsive={true}
responsiveOpen={openFilters}
closeResponsive={toggleFilters}
/> */}

<DrawerContainer
ref={drawerRef}
showCreateOfferModal={setShowCreateOfferModal}
/>
{showCreateOfferModal && (
<CreateOffer closeCreateOfferModal={closeCreateOfferModal} />
)}

+ 1
- 15
src/components/Header/Header.styled.js Ver fichero

@@ -1,6 +1,5 @@
import { Box } from "@mui/material";
import styled from "styled-components";
import { PrimaryButton } from "../Buttons/PrimaryButton/PrimaryButton";


export const DrawerContainer = styled(Box)`
@@ -57,20 +56,7 @@ export const ToolsContainer = styled(Box)`
${(props) => props.mobile && `width: auto;`}
}
`;
export const RegisterButton = styled(PrimaryButton)`
height: 49px;
width: 180px;
font-weight: 600;
@media (max-width: 550px) {
margin-bottom: 20px;
}
`;
export const LoginButton = styled(PrimaryButton)`
height: 49px;
width: 180px;
font-weight: 600;
margin-right: 10px;
`;

export const AuthButtonsContainer = styled(Box)`
display: flex;
justify-content: flex-start;

+ 32
- 0
src/components/Header/LoginButton/LoginButton.js Ver fichero

@@ -0,0 +1,32 @@
import React from "react";
import PropTypes from "prop-types";
import { LoginButtonContainer } from "./LoginButton.styled";
import selectedTheme from "../../../themes";
import { useTranslation } from "react-i18next";
import { LOGIN_PAGE } from "../../../constants/pages";
import history from "../../../store/utils/history";

const LoginButton = () => {
const { t } = useTranslation();
const handleNavigateLogin = () => {
history.push(LOGIN_PAGE);
};
return (
<LoginButtonContainer
type="submit"
variant="contained"
fullWidth
buttoncolor={selectedTheme.colors.primaryPurple}
textcolor="white"
onClick={handleNavigateLogin}
>
{t("login.headerTitle")}
</LoginButtonContainer>
);
};

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

export default LoginButton;

+ 9
- 0
src/components/Header/LoginButton/LoginButton.styled.js Ver fichero

@@ -0,0 +1,9 @@
import styled from "styled-components";
import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton";

export const LoginButtonContainer = styled(PrimaryButton)`
height: 49px;
width: 180px;
font-weight: 600;
margin-right: 10px;
`;

+ 43
- 12
src/components/Header/MyMessagesButton/MyMessagesButton.js Ver fichero

@@ -4,20 +4,51 @@ import selectedTheme from "../../../themes";
import { IconButton } from "../../Buttons/IconButton/IconButton";
import MailIcon from "@mui/icons-material/EmailOutlined";
import { Badge } from "@mui/material";
import { useState } from "react";
import PopoverComponent from "../../Popovers/PopoverComponent";
import { MyMessages } from "../../Popovers/MyMessages/MyMessages";
import { useLocation } from "react-router-dom";
import { useEffect } from "react";

const MyMessagesButton = (props) => {
const MyMessagesButton = () => {
const location = useLocation();
const [msgPopoverOpen, setMsgPopoverOpen] = useState(false);
const [msgAnchorEl, setMsgAnchorEl] = useState(null);
useEffect(() => {
setMsgPopoverOpen(false);
}, [location.pathname]);
const openMsgPopover = (event) => {
setMsgPopoverOpen(true);
setMsgAnchorEl(event.currentTarget);
};

const closeMsgPopover = () => {
setMsgPopoverOpen(false);
setMsgAnchorEl(null);
};
return (
<IconButton
onClick={props.onClick}
style={{
background: selectedTheme.colors.primaryIconBackgroundColor,
color: selectedTheme.colors.primaryPurple,
}}
>
<Badge color="primary">
<MailIcon />
</Badge>
</IconButton>
<>
<IconButton
onClick={openMsgPopover}
style={{
background: selectedTheme.colors.primaryIconBackgroundColor,
color: selectedTheme.colors.primaryPurple,
}}
>
<Badge color="primary">
<MailIcon />
</Badge>
</IconButton>
<PopoverComponent
anchorEl={msgAnchorEl}
open={msgPopoverOpen}
onClose={() => {
setMsgPopoverOpen(false);
setMsgAnchorEl(null);
}}
content={<MyMessages closePopover={closeMsgPopover} />}
/>
</>
);
};


+ 42
- 10
src/components/Header/MySwapsButton/MySwapsButton.js Ver fichero

@@ -3,18 +3,50 @@ import PropTypes from "prop-types";
import selectedTheme from "../../../themes";
import { IconButton } from "../../Buttons/IconButton/IconButton";
import { SwapsIcon } from "./MySwapsButton.styled";
import PopoverComponent from "../../Popovers/PopoverComponent";
import { MyPosts } from "../../Popovers/MyPosts/MyPosts";
import { useState } from "react";
import { useEffect } from "react";
import { useLocation } from "react-router-dom";

const MySwapsButton = (props) => {
const MySwapsButton = () => {
const location = useLocation();
const [postsPopoverOpen, setPostsPopoverOpen] = useState(false);
const [postsAnchorEl, setPostsAnchorEl] = useState(null);

useEffect(() => {
setPostsPopoverOpen(false);
}, [location.pathname]);

const openPostsPopover = (event) => {
setPostsPopoverOpen(true);
setPostsAnchorEl(event.currentTarget);
};
const closePostsPopover = () => {
setPostsPopoverOpen(false);
setPostsAnchorEl(null);
};
return (
<IconButton
onClick={props.onClick}
style={{
background: selectedTheme.colors.primaryIconBackgroundColor,
color: selectedTheme.colors.primaryPurple,
}}
>
<SwapsIcon />
</IconButton>
<>
<IconButton
onClick={openPostsPopover}
style={{
background: selectedTheme.colors.primaryIconBackgroundColor,
color: selectedTheme.colors.primaryPurple,
}}
>
<SwapsIcon />
</IconButton>
<PopoverComponent
anchorEl={postsAnchorEl}
open={postsPopoverOpen}
onClose={() => {
setPostsPopoverOpen(false);
setPostsAnchorEl(null);
}}
content={<MyPosts closePopover={closePostsPopover} />}
/>
</>
);
};


+ 32
- 0
src/components/Header/RegisterButton/RegisterButton.js Ver fichero

@@ -0,0 +1,32 @@
import React from "react";
import PropTypes from "prop-types";
import selectedTheme from "../../../themes";
import { RegisterButtonContainer } from "./RegisterButton.styled";
import { useTranslation } from "react-i18next";
import { REGISTER_PAGE } from "../../../constants/pages";
import history from "../../../store/utils/history";

const RegisterButton = () => {
const { t } = useTranslation();
const handleNavigateRegister = () => {
history.push(REGISTER_PAGE);
};
return (
<RegisterButtonContainer
type="submit"
variant="contained"
fullWidth
buttoncolor={selectedTheme.colors.primaryYellow}
textcolor={selectedTheme.colors.yellowButtonTextColor}
onClick={handleNavigateRegister}
>
{t("register.headerTitle")}
</RegisterButtonContainer>
);
};

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

export default RegisterButton;

+ 11
- 0
src/components/Header/RegisterButton/RegisterButton.styled.js Ver fichero

@@ -0,0 +1,11 @@
import styled from "styled-components";
import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton";

export const RegisterButtonContainer = styled(PrimaryButton)`
height: 49px;
width: 180px;
font-weight: 600;
@media (max-width: 550px) {
margin-bottom: 20px;
}
`;

+ 44
- 12
src/components/Header/UserButton/UserButton.js Ver fichero

@@ -4,26 +4,58 @@ import selectedTheme from "../../../themes";
import { AccountCircle } from "@mui/icons-material";
import { IconButton } from "../../Buttons/IconButton/IconButton";
import { UserButtonContainer, UserName } from "./UserButton.styled";
import PopoverComponent from "../../Popovers/PopoverComponent";
import { MyProfile } from "../../Popovers/MyProfile/MyProfile";
import { useState } from "react";
import { useEffect } from "react";
import { useLocation } from "react-router-dom";

const UserButton = (props) => {
const location = useLocation();
const [userPopoverOpen, setUserPopoverOpen] = useState(false);
const [userAnchorEl, setUserAnchorEl] = useState(null);

useEffect(() => {
setUserPopoverOpen(false);
}, [location.pathname]);

const openUserPopover = (event) => {
setUserPopoverOpen(true);
setUserAnchorEl(event.currentTarget);
};
const closeUserPopover = () => {
setUserPopoverOpen(false);
setUserAnchorEl(null);
};
return (
<UserButtonContainer onClick={props.onClick}>
<UserName>{props.name}</UserName>
<IconButton
style={{
background: selectedTheme.colors.primaryIconBackgroundColor,
color: selectedTheme.colors.primaryPurple,
<>
<UserButtonContainer onClick={openUserPopover}>
<UserName>{props.name}</UserName>
<IconButton
style={{
background: selectedTheme.colors.primaryIconBackgroundColor,
color: selectedTheme.colors.primaryPurple,
}}
>
<AccountCircle />
</IconButton>
</UserButtonContainer>
<PopoverComponent
anchorEl={userAnchorEl}
open={userPopoverOpen}
onClose={() => {
setUserPopoverOpen(false);
setUserAnchorEl(null);
}}
>
<AccountCircle />
</IconButton>
</UserButtonContainer>
content={<MyProfile closePopover={closeUserPopover} />}
/>
</>
);
};

UserButton.propTypes = {
onClick: PropTypes.func,
name: PropTypes.string,
onClick: PropTypes.func,
name: PropTypes.string,
};

export default UserButton;

+ 0
- 32
src/components/IconButton/IconButton.js Ver fichero

@@ -1,32 +0,0 @@
import React, { useRef } from 'react';
import PropType from 'prop-types';

const IconButton = ({ children, onClick, className }) => {
const buttonRef = useRef(null);

function handleClick(event) {
buttonRef.current.blur();
if (typeof onClick === 'function') {
onClick(event);
}
}

return (
<button
type="button"
ref={buttonRef}
onClick={handleClick}
className={`c-icon-button ${className && className}`}
>
{children}
</button>
);
};

IconButton.propTypes = {
children: PropType.node,
onClick: PropType.func,
className: PropType.string,
};

export default IconButton;

+ 1
- 1
src/components/ImagePicker/ImagePicker.js Ver fichero

@@ -30,7 +30,7 @@ const ImagePicker = (props) => {
}
}, [props.image]);

let listener = useCallback(
const listener = useCallback(
(event) => {
if (imageRef.current) {
if (imageRef.current.contains(event.target)) {

+ 5
- 15
src/components/Login/Fields/Password/PasswordField.js Ver fichero

@@ -1,12 +1,12 @@
import React, { forwardRef, useEffect, 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";
import { IconButton } from "../../../Buttons/IconButton/IconButton";

const PasswordField = forwardRef((props, ref) => {
const formik = props.formik;
@@ -14,22 +14,11 @@ const PasswordField = forwardRef((props, ref) => {
const handleClickShowPassword = () => setShowPassword(!showPassword);
const handleMouseDownPassword = () => setShowPassword(!showPassword);
const error = useSelector(selectLoginError);
const {t} = useTranslation();
// useEffect(() => {
// console.log("error", error);
// console.log("formik errors password", formik.errors.password);
// console.log("formik touched", formik.touched);
// console.log("formik.isVaid", formik);
// if (!formik.isValid) formik.setFieldValue("password", "");
// // if (error?.length > 0 || formik.errors.password) {
// // formik.setFieldValue("password", "");
// // console.log(formik.errors.password)
// // }
// }, [formik.isValid])
const { t } = useTranslation();

useEffect(() => {
console.dir(error);
}, [error])
}, [error]);

return (
<TextField
@@ -41,7 +30,8 @@ const PasswordField = forwardRef((props, ref) => {
value={formik.values.password}
onChange={formik.handleChange}
error={
(formik.touched.password && formik.errors.password?.length > 0) || error.length > 0
(formik.touched.password && formik.errors.password?.length > 0) ||
error.length > 0
}
helperText={formik.touched.password && formik.errors.password}
fullWidth

+ 0
- 2
src/components/MarketPlace/Header/Header.js Ver fichero

@@ -60,7 +60,6 @@ const Header = (props) => {
<>
<SkeletonHeader
skeleton={props.skeleton}
animationStage={props.animationStage}
/>
<HeaderContainer skeleton={props.skeleton}>
{/* Setting appropriate header title if page is market place or my offers */}
@@ -154,7 +153,6 @@ Header.propTypes = {
category: PropTypes.string,
myOffers: PropTypes.bool,
skeleton: PropTypes.bool,
animationStage: PropTypes.number,
sorting: PropTypes.any,
};
Header.defaultProps = {

+ 4
- 5
src/components/MarketPlace/Header/SkeletonHeader/SkeletonHeader.js Ver fichero

@@ -5,13 +5,13 @@ import { CircleGroup, SkeletonHeaderCircle, SkeletonHeaderContainer, SkeletonHea
const SkeletonHeader = (props) => {
return (
<SkeletonHeaderContainer skeleton={props.skeleton}>
<SkeletonHeaderLine animationStage={props.animationStage} />
<SkeletonHeaderLine />
<SkeletonRowGroup>
<CircleGroup>
<SkeletonHeaderCircle animationStage={props.animationStage} />
<SkeletonHeaderCircle animationStage={props.animationStage} />
<SkeletonHeaderCircle />
<SkeletonHeaderCircle />
</CircleGroup>
<SkeletonHeaderRightLine animationStage={props.animationStage} />
<SkeletonHeaderRightLine />
</SkeletonRowGroup>
</SkeletonHeaderContainer>
)
@@ -19,7 +19,6 @@ const SkeletonHeader = (props) => {

SkeletonHeader.propTypes = {
skeleton: PropTypes.bool,
animationStage: PropTypes.number,
}

export default SkeletonHeader

+ 0
- 3
src/components/MarketPlace/MarketPlace.js Ver fichero

@@ -16,12 +16,10 @@ const MarketPlace = (props) => {
myOffers={props.myOffers}
sorting={props.offers.sorting}
skeleton={props.skeleton}
animationStage={props.animationStage}
/>
<Offers
isGrid={isGrid}
myOffers={props.myOffers}
animationStage={props.animationStage}
skeleton={props.skeleton}
offers={offers}
toggleFilters={props.toggleFilters}
@@ -33,7 +31,6 @@ const MarketPlace = (props) => {
MarketPlace.propTypes = {
children: PropTypes.node,
myOffers: PropTypes.bool,
animationStage: PropTypes.number,
skeleton: PropTypes.bool,
offers: PropTypes.any,
toggleFilters: PropTypes.func

+ 0
- 2
src/components/MarketPlace/Offers/Offers.js Ver fichero

@@ -73,7 +73,6 @@ const Offers = (props) => {
<SkeletonOfferCard
key={index}
skeleton
animationStage={props.animationStage}
/>
))}
</>
@@ -87,7 +86,6 @@ Offers.propTypes = {
isGrid: PropTypes.bool,
myOffers: PropTypes.bool,
skeleton: PropTypes.bool,
animationStage: PropTypes.number,
offers: PropTypes.any,
toggleFilters: PropTypes.func,
};

+ 1
- 1
src/components/Profile/Profile.js Ver fichero

@@ -1,7 +1,7 @@
import React, { useEffect, useMemo } from "react";
import PropTypes from "prop-types";
import { ProfileContainer } from "./Profile.styled";
import ProfileCard from "../ProfileCard/ProfileCard";
import ProfileCard from "../Cards/ProfileCard/ProfileCard";
import ProfileOffers from "./ProfileOffers/ProfileOffers";
import { useDispatch, useSelector } from "react-redux";
import { selectUserId } from "../../store/selectors/loginSelectors";

+ 26
- 0
src/components/Profile/ProfileOffers/HeaderTitle/HeaderTitle.js Ver fichero

@@ -0,0 +1,26 @@
import React from "react";
import PropTypes from "prop-types";
import {
HeaderTitleContainer,
HeaderTitleText,
OffersIcon,
} from "./HeaderTitle.styled";
import { useTranslation } from "react-i18next";

const HeaderTitle = (props) => {
const { t } = useTranslation();
return (
<HeaderTitleContainer container>
<OffersIcon />
<HeaderTitleText>
{props.isMyProfile ? t("profile.myOffers") : t("profile.profileOffers")}
</HeaderTitleText>
</HeaderTitleContainer>
);
};

HeaderTitle.propTypes = {
isMyProfile: PropTypes.bool,
};

export default HeaderTitle;

+ 32
- 0
src/components/Profile/ProfileOffers/HeaderTitle/HeaderTitle.styled.js Ver fichero

@@ -0,0 +1,32 @@
import { Grid, Typography } from "@mui/material";
import styled from "styled-components";
import { ReactComponent as Package } from "../../../../assets/images/svg/package-gray.svg";
import selectedTheme from "../../../../themes";

export const HeaderTitleText = styled(Typography)`
font-size: 16px;
font-family: ${selectedTheme.fonts.textFont};
color: ${selectedTheme.colors.primaryDarkTextThird};
position: relative;
margin-left: 10px;
@media (max-width: 600px) {
font-size: 12px;
}
`;
export const OffersIcon = styled(Package)`
width: 18px;
height: 18px;
& path {
stroke: ${selectedTheme.colors.primaryDarkTextThird};
}
@media (max-width: 600px) {
width: 12px;
height: 12px;
}
`;
export const HeaderTitleContainer = styled(Grid)`
flex-direction: row;
justify-content: start;
align-items: center;
margin-bottom: 8px;
`

+ 21
- 120
src/components/Profile/ProfileOffers/ProfileOffers.js Ver fichero

@@ -1,26 +1,14 @@
import React from "react";
import PropTypes from "prop-types";
import {
DownArrow,
HeaderSelect,
HeaderTitle,
IconContainer,
OffersContainer,
OffersIcon,
OffersScroller,
ProfileOffersContainer,
SearchIcon,
SearchInput,
SelectOption,
} from "./ProfileOffers.styled";
import { Grid } from "@mui/material";
import { useState } from "react";
import { sortEnum } from "../../../enums/sortEnum";
import { useEffect } from "react";
import { useSelector } from "react-redux";
import OfferCard from "../../Cards/OfferCard/OfferCard";
import { useTranslation } from "react-i18next";
import { useRef } from "react";
import { selectProfileOffers } from "../../../store/selectors/offersSelectors";
import { selectLatestChats } from "../../../store/selectors/chatSelectors";
import { useHistory } from "react-router-dom";
@@ -31,19 +19,21 @@ import { OFFERS_PROFILE_SCOPE } from "../../../store/actions/offers/offersAction
import SkeletonOfferCard from "../../Cards/OfferCard/SkeletonOfferCard/SkeletonOfferCard";
import useIsMobile from "../../../hooks/useIsMobile";
import ProfileOffersHeaderSkeleton from "./ProfileOffersHeaderSkeleton/ProfileOffersHeaderSkeleton";
import SelectSortField from "./SelectSortField/SelectSortField";
import HeaderTitle from "./HeaderTitle/HeaderTitle";
import SearchBar from "./SearchBar/SearchBar";
import { replaceInRoute } from "../../../util/helpers/routeHelpers";
import { CHAT_MESSAGE_PAGE } from "../../../constants/pages";

const ProfileOffers = (props) => {
const [sortOption, setSortOption] = useState(sortEnum.INITIAL);
const [offersToShow, setOffersToShow] = useState([]);
const isLoadingMineOffers = useSelector(
selectIsLoadingByActionType(OFFERS_PROFILE_SCOPE)
);
const searchRef = useRef(null);
const chats = useSelector(selectLatestChats);
const profileOffers = useSelector(selectProfileOffers);
const { isMobile } = useIsMobile();
const history = useHistory();
const { t } = useTranslation();
const userId = useSelector(selectUserId);
const arrayForMapping = Array.apply(null, Array(4)).map(() => {});

@@ -52,133 +42,44 @@ const ProfileOffers = (props) => {
(item) => item.chat.offerId === offer?.offer?._id
);
if (chatItem !== undefined) {
history.push(`/messages/${chatItem.chat._id}`);
history.push(
replaceInRoute(CHAT_MESSAGE_PAGE, { idChat: chatItem.chat._id })
);
} else {
if (offer?.offer?.userId !== userId) {
history.push(`/messages/newMessage`, {
offerId: offer?.offer?._id,
});
history.push(
replaceInRoute(CHAT_MESSAGE_PAGE, { idChat: "newMessage" }),
{
offerId: offer?.offer?._id,
}
);
}
}
};

useEffect(() => {
let newOffersToShow = [...offersToShow];
if (sortOption.value === sortEnum.OLD.value) {
newOffersToShow.sort(
(a, b) => new Date(a._created) - new Date(b._created)
);
}
if (sortOption.value === sortEnum.NEW.value) {
newOffersToShow.sort(
(a, b) => new Date(b._created) - new Date(a._created)
);
}
if (sortOption.value === sortEnum.POPULAR.value) {
newOffersToShow.sort(
(a, b) => a.views.viewers.length - b.views.viewers.length
);
}
setOffersToShow([...newOffersToShow]);
}, [sortOption]);

useEffect(() => {
if (profileOffers) setOffersToShow(profileOffers);
}, [profileOffers]);

const handleSearch = () => {
const valueToSearch = searchRef?.current?.value;
const handleSearch = (value) => {
let newOffersToShow = profileOffers.filter((item) =>
item.name.toLowerCase().includes(valueToSearch.toLowerCase())
item.name.toLowerCase().includes(value.toLowerCase())
);
setOffersToShow([...newOffersToShow]);
};

const handleChangeSelect = (event) => {
let chosenOption;
for (const sortOption in sortEnum) {
if (sortEnum[sortOption].value === event.target.value) {
chosenOption = sortEnum[sortOption];
setSortOption(chosenOption);
}
}
};

let listener;
const handleFocusSearch = () => {
listener = (event) => {
if (event.keyCode === 13) {
event.preventDefault();
handleSearch();
}
};
searchRef.current.addEventListener("keyup", listener);
};

const handleBlurSearch = () => {
searchRef.current.removeEventListener("keyup", listener);
};

return (
<ProfileOffersContainer>
{isLoadingMineOffers || isLoadingMineOffers === undefined ? (
<ProfileOffersHeaderSkeleton />
) : (
<>
<HeaderSelect
value={
sortOption?.value ? sortOption.value : sortEnum.INITIAL.value
}
IconComponent={DownArrow}
onChange={handleChangeSelect}
>
<SelectOption
value={sortEnum.INITIAL.value}
key={sortEnum.INITIAL.value}
style={{ display: "none" }}
>
{sortEnum.INITIAL.mainText}
</SelectOption>
{Object.keys(sortEnum).map((property) => {
if (sortEnum[property].value === sortEnum.INITIAL.value) return;
return (
<SelectOption
value={sortEnum[property].value}
key={sortEnum[property].value}
>
{sortEnum[property].mainText}
</SelectOption>
);
})}
</HeaderSelect>
<Grid
container
direction="row"
justifyContent="start"
alignItems="center"
sx={{ mb: 1.4 }}
>
<OffersIcon />
<HeaderTitle>
{props.isMyProfile ? "Moje objave" : "Objave kompanije"}
</HeaderTitle>
</Grid>
<SearchInput
fullWidth
ref={searchRef}
onFocus={handleFocusSearch}
onBlur={handleBlurSearch}
// ref={searchRef}
placeholder={t("header.searchOffers")}
italicPlaceholder
InputProps={{
endAdornment: (
<IconContainer onClick={handleSearch}>
<SearchIcon />
</IconContainer>
),
}}
<SelectSortField
offersToShow={offersToShow}
setOffersToShow={setOffersToShow}
/>
<HeaderTitle isMyProfile={props.isMyProfile} />
<SearchBar handleSearch={handleSearch} />
</>
)}
<OffersContainer>

+ 1
- 85
src/components/Profile/ProfileOffers/ProfileOffers.styled.js Ver fichero

@@ -1,13 +1,5 @@
import { Box, Typography } from "@mui/material";
import { Box } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../themes";
import { ReactComponent as Search } from "../../../assets/images/svg/magnifying-glass.svg";
import { ReactComponent as Package } from "../../../assets/images/svg/package-gray.svg";
import { TextField } from "../../TextFields/TextField/TextField";
import { Icon } from "../../Icon/Icon";
import Select from "../../Select/Select";
import Option from "../../Select/Option/Option";
import { ReactComponent as Down } from "../../../assets/images/svg/down-arrow.svg";
import HorizontalScroller from "../../Scroller/HorizontalScroller";

export const ProfileOffersContainer = styled(Box)`
@@ -23,82 +15,6 @@ export const ProfileOffersContainer = styled(Box)`
padding: 0;
}
`;
export const HeaderTitle = styled(Typography)`
font-size: 16px;
font-family: ${selectedTheme.fonts.textFont};
color: ${selectedTheme.colors.primaryDarkTextThird};
position: relative;
margin-left: 10px;
@media (max-width: 600px) {
font-size: 12px;
}
`;
export const OffersIcon = styled(Package)`
width: 18px;
height: 18px;
& path {
stroke: ${selectedTheme.colors.primaryDarkTextThird};
}
@media (max-width: 600px) {
width: 12px;
height: 12px;
}
`;
export const SearchInput = styled(TextField)`
position: relative;
top: 15px;
& div fieldset {
border-color: ${selectedTheme.colors.primaryPurple} !important;
}
@media (max-width: 600px) {
top: 5px;
height: 46px;
& div {
background-color: white;
}
}
`;
export const SearchIcon = styled(Search)`
width: 18px;
height: 18px;
`;
export const IconContainer = styled(Icon)`
cursor: pointer;
position: relative;
top: 4px;
`;
export const HeaderSelect = styled(Select)`
width: 210px;
height: 35px;
font-family: ${selectedTheme.fonts.textFont};
margin-top: 3px;
font-weight: 400;
position: absolute;
top: -8px;
right: 50px;
& div:first-child {
padding-left: 8px;
}

@media (max-width: 1200px) {
right: 36px;
}

@media (max-width: 650px) {
width: 144px;
height: 30px;
font-size: 14px;
right: 1px;
}
`;
export const SelectOption = styled(Option)`
@media (max-width: 600px) {
height: 20px !important;
min-height: 35px;
margin: 2px;
}
`;
export const DownArrow = styled(Down)``;
export const OffersContainer = styled(Box)`
margin-top: 30px;
`;

+ 24
- 16
src/components/Profile/ProfileOffers/ProfileOffersHeaderSkeleton/ProfileOffersHeaderSkeleton.js Ver fichero

@@ -1,24 +1,32 @@
import React from 'react'
import PropTypes from 'prop-types'
import { ProfileOffersHeaderSkeletonContainer, ProfileOffersHeaderSkeletonGroupOne, ProfileOffersHeaderSkeletonGroupSecond, ProfileOffersHeaderSkeletonLineForth, ProfileOffersHeaderSkeletonLineOne, ProfileOffersHeaderSkeletonLineSecond, ProfileOffersHeaderSkeletonLineThird } from './ProfileOffersHeaderSkeleton.styled'
import React from "react";
import PropTypes from "prop-types";
import {
ProfileOffersHeaderSkeletonContainer,
ProfileOffersHeaderSkeletonGroupOne,
ProfileOffersHeaderSkeletonGroupSecond,
ProfileOffersHeaderSkeletonLineForth,
ProfileOffersHeaderSkeletonLineOne,
ProfileOffersHeaderSkeletonLineSecond,
ProfileOffersHeaderSkeletonLineThird,
} from "./ProfileOffersHeaderSkeleton.styled";

const ProfileOffersHeaderSkeleton = () => {
return (
<ProfileOffersHeaderSkeletonContainer>
<ProfileOffersHeaderSkeletonGroupOne>
<ProfileOffersHeaderSkeletonLineOne />
<ProfileOffersHeaderSkeletonLineSecond />
</ProfileOffersHeaderSkeletonGroupOne>
<ProfileOffersHeaderSkeletonGroupSecond>
<ProfileOffersHeaderSkeletonLineThird />
<ProfileOffersHeaderSkeletonLineForth />
</ProfileOffersHeaderSkeletonGroupSecond>
<ProfileOffersHeaderSkeletonGroupOne>
<ProfileOffersHeaderSkeletonLineOne />
<ProfileOffersHeaderSkeletonLineSecond />
</ProfileOffersHeaderSkeletonGroupOne>
<ProfileOffersHeaderSkeletonGroupSecond>
<ProfileOffersHeaderSkeletonLineThird />
<ProfileOffersHeaderSkeletonLineForth />
</ProfileOffersHeaderSkeletonGroupSecond>
</ProfileOffersHeaderSkeletonContainer>
)
}
);
};

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

export default ProfileOffersHeaderSkeleton
export default ProfileOffersHeaderSkeleton;

+ 1
- 1
src/components/Profile/ProfileOffers/ProfileOffersHeaderSkeleton/ProfileOffersHeaderSkeleton.styled.js Ver fichero

@@ -3,7 +3,7 @@ import styled from "styled-components";
import {
SkeletonBackgroundColor,
SkeletonItemBackgroundColor,
} from "../../../ProfileCard/SkeletonProfileCard/SkeletonProfileCard.styled";
} from "../../../Cards/ProfileCard/SkeletonProfileCard/SkeletonProfileCard.styled";

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

+ 53
- 0
src/components/Profile/ProfileOffers/SearchBar/SearchBar.js Ver fichero

@@ -0,0 +1,53 @@
import React from "react";
import PropTypes from "prop-types";
import { IconContainer, SearchIcon, SearchInput } from "./SearchBar.styled";
import { useCallback } from "react";
import { useRef } from "react";
import { useTranslation } from "react-i18next";

const SearchBar = (props) => {
const searchRef = useRef(null);
const { t } = useTranslation();
let listener = useCallback(
(event) => {
if (event.keyCode === 13) {
event.preventDefault();
props.handleSearch(searchRef.current?.value);
}
},
[searchRef]
);

const handleFocusSearch = () => {
searchRef.current.addEventListener("keyup", listener);
};
const handleBlurSearch = () => {
searchRef.current.removeEventListener("keyup", listener);
};

return (
<SearchInput
fullWidth
ref={searchRef}
onFocus={handleFocusSearch}
onBlur={handleBlurSearch}
placeholder={t("header.searchOffers")}
italicPlaceholder
InputProps={{
endAdornment: (
<IconContainer
onClick={() => props.handleSearch(searchRef.current?.value)}
>
<SearchIcon />
</IconContainer>
),
}}
/>
);
};

SearchBar.propTypes = {
handleSearch: PropTypes.func,
};

export default SearchBar;

+ 29
- 0
src/components/Profile/ProfileOffers/SearchBar/SearchBar.styled.js Ver fichero

@@ -0,0 +1,29 @@
import styled from "styled-components";
import selectedTheme from "../../../../themes";
import { TextField } from "../../../TextFields/TextField/TextField";
import { ReactComponent as Search } from "../../../../assets/images/svg/magnifying-glass.svg";
import { Icon } from "../../../Icon/Icon";

export const SearchInput = styled(TextField)`
position: relative;
top: 15px;
& div fieldset {
border-color: ${selectedTheme.colors.primaryPurple} !important;
}
@media (max-width: 600px) {
top: 5px;
height: 46px;
& div {
background-color: white;
}
}
`;
export const SearchIcon = styled(Search)`
width: 18px;
height: 18px;
`;
export const IconContainer = styled(Icon)`
cursor: pointer;
position: relative;
top: 4px;
`;

+ 76
- 0
src/components/Profile/ProfileOffers/SelectSortField/SelectSortField.js Ver fichero

@@ -0,0 +1,76 @@
import React from "react";
import PropTypes from "prop-types";
import {
DownArrow,
HeaderSelect,
SelectOption,
} from "./SelectSortField.styled";
import { sortEnum } from "../../../../enums/sortEnum";
import { useState } from "react";
import { useEffect } from "react";

const SelectSortField = (props) => {
const [sortOption, setSortOption] = useState(sortEnum.INITIAL);

useEffect(() => {
let newOffersToShow = [...props.offersToShow];
if (sortOption.value === sortEnum.OLD.value) {
newOffersToShow.sort(
(a, b) => new Date(a._created) - new Date(b._created)
);
}
if (sortOption.value === sortEnum.NEW.value) {
newOffersToShow.sort(
(a, b) => new Date(b._created) - new Date(a._created)
);
}
if (sortOption.value === sortEnum.POPULAR.value) {
newOffersToShow.sort((a, b) => b.views.count - a.views.count);
}
props.setOffersToShow([...newOffersToShow]);
}, [sortOption]);
const handleChangeSelect = (event) => {
let chosenOption;
console.log(sortOption);
for (const sortOption in sortEnum) {
if (sortEnum[sortOption].value === event.target.value) {
chosenOption = sortEnum[sortOption];
console.log(chosenOption);
setSortOption(chosenOption);
}
}
};
return (
<HeaderSelect
value={sortOption?.value ? sortOption.value : sortEnum.INITIAL.value}
IconComponent={DownArrow}
onChange={handleChangeSelect}
>
<SelectOption
value={sortEnum.INITIAL.value}
key={sortEnum.INITIAL.value}
style={{ display: "none" }}
>
{sortEnum.INITIAL.mainText}
</SelectOption>
{Object.keys(sortEnum).map((property) => {
if (sortEnum[property].value === sortEnum.INITIAL.value) return;
return (
<SelectOption
value={sortEnum[property].value}
key={sortEnum[property].value}
>
{sortEnum[property].mainText}
</SelectOption>
);
})}
</HeaderSelect>
);
};

SelectSortField.propTypes = {
offersToShow: PropTypes.array,
setOffersToShow: PropTypes.func,
};

export default SelectSortField;

+ 38
- 0
src/components/Profile/ProfileOffers/SelectSortField/SelectSortField.styled.js Ver fichero

@@ -0,0 +1,38 @@
import styled from "styled-components";
import Select from "../../../Select/Select";
import Option from "../../../Select/Option/Option";
import { ReactComponent as Down } from "../../../../assets/images/svg/down-arrow.svg";
import selectedTheme from "../../../../themes";

export const HeaderSelect = styled(Select)`
width: 210px;
height: 35px;
font-family: ${selectedTheme.fonts.textFont};
margin-top: 3px;
font-weight: 400;
position: absolute;
top: -8px;
right: 50px;
& div:first-child {
padding-left: 8px;
}

@media (max-width: 1200px) {
right: 36px;
}

@media (max-width: 650px) {
width: 144px;
height: 30px;
font-size: 14px;
right: 1px;
}
`;
export const SelectOption = styled(Option)`
@media (max-width: 600px) {
height: 20px !important;
min-height: 35px;
margin: 2px;
}
`;
export const DownArrow = styled(Down)``;

+ 0
- 274
src/components/ProfileCard/EditProfile/EditProfile.js Ver fichero

@@ -1,274 +0,0 @@
import React, { useState, useEffect } from "react";
import PropTypes from "prop-types";
import BackdropComponent from "../../MUI/BackdropComponent";
import {
EditProfileContainer,
ProfileImageContainer,
InputFieldLabel,
InputField,
BackButton,
CloseButton,
SaveButton,
ProfileHeader,
BasicInfo,
DetailsInfo,
ButtonsContainer,
ErrorMessage,
ProfileImagePicker,
InputFieldLabelLocation,
} from "./EditProfile.styled";
import selectedTheme from "../../../themes";
import { useFormik } from "formik";
import { ReactComponent as ArrowBack } from "../../../assets/images/svg/arrow-back.svg";
import { ReactComponent as CloseIcon } from "../../../assets/images/svg/close-modal.svg";
import { useTranslation } from "react-i18next";
import {
editMineProfile,
fetchMineProfile,
} from "../../../store/actions/profile/profileActions";
import { useDispatch, useSelector } from "react-redux";
import { selectUserId } from "../../../store/selectors/loginSelectors";
import editProfileValidation from "../../../validations/editProfileValidation";
import useIsMobile from "../../../hooks/useIsMobile";
import AutoSuggestTextField from "../../TextFields/AutoSuggestTextField/AutoSuggestTextField";
import { selectLocations } from "../../../store/selectors/locationsSelectors";

const EditProfile = (props) => {
const [profileImage, setProfileImage] = useState(props.profile.image);
const [showBasic, setShowBasic] = useState(true);
const [showDetails, setShowDetails] = useState(true);
const { t } = useTranslation();
const dispatch = useDispatch();
const { isMobile } = useIsMobile();
const userId = useSelector(selectUserId);
const locations = useSelector(selectLocations);

useEffect(() => {
if (isMobile) {
setShowDetails(false);
} else {
setShowDetails(true);
}
}, [isMobile]);

const handleApiResponseSuccess = () => {
dispatch(fetchMineProfile(userId));
props.reFetchProfile();
};

const handleSubmit = (values) => {
dispatch(editMineProfile({ ...values, handleApiResponseSuccess }));
props.closeModalHandler();
};

const formik = useFormik({
initialValues: {
firmName: `${props?.profile?.company?.name}`,
firmPIB: `${props?.profile?.company?.PIB}`,
firmLocation: `${props?.profile?.company?.contacts?.location ?? ""}`,
firmWebsite: `${props?.profile?.company?.contacts?.web ?? ""}`,
firmApplink: "",
firmPhone: `${props?.profile?.company?.contacts?.telephone ?? ""}`,
firmLogo: profileImage,
},
validationSchema: editProfileValidation,
onSubmit: handleSubmit,
validateOnBlur: true,
enableReinitialize: true,
});

const closeEditModalHandler = () => {
props.closeModalHandler();
};

const showDetailsHandler = () => {
setShowDetails(!showDetails);
setShowBasic(!showBasic);
};

const setImage = (image) => {
setProfileImage(image);
};

return (
<>
<BackdropComponent
handleClose={closeEditModalHandler}
isLoading
position="fixed"
/>
<EditProfileContainer component="form" onSubmit={formik.handleSubmit}>
{!showBasic && (
<BackButton onClick={showDetailsHandler}>
<ArrowBack />
</BackButton>
)}
<ProfileImageContainer>
<ProfileImagePicker
image={profileImage}
setImage={setImage}
></ProfileImagePicker>
<ProfileHeader>{props.profile.company.name}</ProfileHeader>
</ProfileImageContainer>
<CloseButton onClick={closeEditModalHandler}>
<CloseIcon />
</CloseButton>
{showBasic && (
<BasicInfo>
<InputFieldLabel leftText={t("common.labelFirm").toUpperCase()} />
<InputField
name="firmName"
value={formik.values.firmName}
onChange={formik.handleChange}
error={formik.touched.firmName && formik.errors.firmName}
margin="normal"
fullWidth
/>
<InputFieldLabel leftText={t("common.labelPIB")} />
<InputField
name="firmPIB"
type="number"
value={formik.values.firmPIB}
onChange={formik.handleChange}
error={formik.touched.firmPIB && formik.errors.firmPIB}
margin="normal"
fullWidth
disabled
/>
<InputFieldLabelLocation
leftText={t("common.labelLocation").toUpperCase()}
/>
<AutoSuggestTextField
editLocation
data={locations.map((item) => ({ name: item.city }))}
value={formik.values.firmLocation}
onChange={(event, { newValue }) =>
formik.setFieldValue("firmLocation", newValue)
}
/>
{/* <InputField
name="firmLocation"
value={formik.values.firmLocation}
onChange={formik.handleChange}
error={formik.touched.firmLocation && formik.errors.firmLocation}
margin="normal"
fullWidth
/> */}
</BasicInfo>
)}
{showDetails && (
<DetailsInfo>
<InputFieldLabel
leftText={t("editProfile.website").toUpperCase()}
labelWebsite
/>
<InputField
name="firmWebsite"
value={formik.values.firmWebsite}
onChange={formik.handleChange}
margin="normal"
fullWidth
/>
<InputFieldLabel
leftText={t("editProfile.applink").toUpperCase()}
/>
<InputField
name="firmApplink"
values={formik.values.firmApplink}
margin="normal"
fullWidth
/>
<InputFieldLabel
leftText={t("editProfile.phoneNumber").toUpperCase()}
/>
<InputField
type="number"
name="firmPhone"
value={formik.values.firmPhone}
onChange={(event) => {
formik.setFieldValue("firmPhone", event.target.value);
}}
error={formik.touched.firmPhone && formik.errors.firmPhone}
margin="normal"
fullWidth
onInput={(e) => {
e.target.value =
e.target.value[0] === "0" && e.target.value.length > 1
? "0" +
String(
Math.max(0, parseInt(e.target.value))
.toString()
.slice(0, 14)
)
: Math.max(0, parseInt(e.target.value))
.toString()
.slice(0, 14);
}}
/>
</DetailsInfo>
)}

{formik.errors.firmName && formik.touched.firmName ? (
<ErrorMessage>{formik.errors.firmName}</ErrorMessage>
) : formik.errors.firmPIB && formik.touched.firmPIB ? (
<ErrorMessage>{formik.errors.firmPIB}</ErrorMessage>
) : formik.errors.firmLocation && formik.touched.firmLocation ? (
<ErrorMessage>{formik.errors.firmLocation}</ErrorMessage>
) : formik.errors.firmPhone && formik.touched.firmPhone ? (
<ErrorMessage>{formik.errors.firmPhone}</ErrorMessage>
) : (
<></>
)}

{!isMobile ? (
<ButtonsContainer>
<SaveButton
type="submit"
variant="contained"
height="48px"
width="335px"
buttoncolor={selectedTheme.colors.primaryPurple}
textcolor="white"
>
{t("editProfile.saveChanges")}
</SaveButton>
</ButtonsContainer>
) : (
<ButtonsContainer>
<SaveButton
height="44px"
width="155px"
buttoncolor={selectedTheme.colors.primaryPurple}
textcolor={selectedTheme.colors.primaryPurple}
onClick={showDetailsHandler}
>
{showDetails
? t("editProfile.showBasic")
: t("editProfile.showDetails")}
</SaveButton>
<SaveButton
type="submit"
variant="contained"
height="44px"
width="155px"
buttoncolor={selectedTheme.colors.primaryPurple}
textcolor="white"
>
{t("common.save")}
</SaveButton>
</ButtonsContainer>
)}
</EditProfileContainer>
</>
);
};

EditProfile.propTypes = {
children: PropTypes.node,
profile: PropTypes.any,
closeModalHandler: PropTypes.func,
setImage: PropTypes.func,
reFetchProfile: PropTypes.func,
};

export default EditProfile;

+ 21
- 0
src/components/Router/AuthRoute.js Ver fichero

@@ -0,0 +1,21 @@
import React, { useMemo } from "react";
import { Redirect, Route } from "react-router";
import { useSelector } from "react-redux";
import { BASE_PAGE } from "../../constants/pages";
import { selectUserId } from "../../store/selectors/loginSelectors";

const AuthRoute = ({ ...props }) => {
const userId = useSelector(selectUserId);
const isUserAuthenticated = useMemo(() => {
if (userId?.length === 0) return false;
return true;
}, [userId]);

return !isUserAuthenticated ? (
<Route {...props} />
) : (
<Redirect to={BASE_PAGE} />
);
};

export default AuthRoute;

+ 14
- 11
src/i18n/resources/rs.js Ver fichero

@@ -100,9 +100,10 @@ export default {
welcome: "Dobro došli na trampu, želimo vam uspešno trampovanje!",
imageError: "Slika je obavezna!",
serverError: "Greška sa serverom!",
phoneNumberNoOfCharacters: "Broj telefona mora imati izmedju 6 i 15 karaktera!",
phoneNumberNoOfCharacters:
"Broj telefona mora imati izmedju 6 i 15 karaktera!",
locationError: "Odaberite ispravnu lokaciju!",
websiteError: "Unesite ispravnu adresu svog website!"
websiteError: "Unesite ispravnu adresu svog website!",
},
forgotPassword: {
title: "Povrati lozinku",
@@ -171,11 +172,11 @@ export default {
newOffer: "Nova Objava",
product: "Proizvod",
descriptionLabel: "Opis:",
checkButtonLabel: "Pogledaj proizvod"
checkButtonLabel: "Pogledaj proizvod",
},
apiErrors: {
somethingWentWrong: "Greška sa serverom!",
offerNotFound: "Ponuda nije pronađena!"
offerNotFound: "Ponuda nije pronađena!",
},
header: {
addOffer: "Dodaj proizvod",
@@ -205,7 +206,7 @@ export default {
leaveComment: "Ostavi komentar",
rates: "Ocene kompanije",
finishedReviewTitle: "Hvala vam",
finishedReviewAltTitle: "na izdvojenom vremenu i datoj oceni!"
finishedReviewAltTitle: "na izdvojenom vremenu i datoj oceni!",
},
messages: {
headerTitle: "Moje Ćaskanje",
@@ -214,7 +215,7 @@ export default {
send: "Pošalji",
sendPlaceholder: "Poruka...",
seeChats: "Pogledaj ćaskanje",
noMessagesToast: "Nemate ni jednu poruku!"
noMessagesToast: "Nemate ni jednu poruku!",
},
editProfile: {
website: "Web Sajt*",
@@ -270,11 +271,13 @@ export default {
altText: "Nažalost nemate ni jednu objavu",
},
backToHome: "Nazad na sve objave",
myOffers: "Moje objave",
profileOffers: "Objave kompanije",
},
about: {
header: {
title: "O Trampi",
navigation: 'O nama',
navigation: "O nama",
paragraph:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ac augue tortor. Nulla facilisi. Cras vestibulum risus eget tincidunt egestas. Duis blandit enim sit amet dui vehicula luctus. Suspendisse id blandit arcu, vitae consequat nisi. Duis ut vestibulum tellus. Curabitur eu fringilla nisi.",
},
@@ -315,7 +318,7 @@ export default {
prices: {
header: {
title: "Cenovnik",
navigation: 'Cenovnik',
navigation: "Cenovnik",
link: "Pročitajte opširnije o našim ponudama",
},
altText:
@@ -369,7 +372,7 @@ export default {
},
aboutFooter: {
goStart: "Početak stranice",
middleText: "Diligent Software © 2022 | Sva prava zadržana"
middleText: "Diligent Software © 2022 | Sva prava zadržana",
},
notFoundData: {
PIB: "PIB broj",
@@ -380,6 +383,6 @@ export default {
offerName: "Naslov ponude",
condition: "Stanje",
description: "Opis",
email: "Mejl kompanije"
}
email: "Mejl kompanije",
},
};

+ 9
- 0
src/initialValues/editProfileInitialValues.js Ver fichero

@@ -0,0 +1,9 @@
export default (profile) => ({
firmName: profile?.company?.name,
firmPIB: profile?.company?.PIB,
firmLocation: profile?.company?.contacts?.location ?? "",
firmWebsite: profile?.company?.contacts?.web ?? "",
firmApplink: "",
firmPhone: profile?.company?.contacts?.telephone ?? "",
firmLogo: profile?.image,
})

+ 17
- 16
src/layouts/ProfileLayout/ProfileLayout.js Ver fichero

@@ -1,25 +1,25 @@
import React from "react";
import PropTypes from "prop-types";
import { Content, RightCard, ProfileLayoutContainer, HeaderCard, MiddleCard } from "./ProfileLayout.styled";
import { Grid } from "@mui/material";
import {
ContentRightCardContainer,
Content,
RightCard,
ProfileLayoutContainer,
} from "./ProfileLayout.styled";

const ProfileLayout = (props) => {
return (
<ProfileLayoutContainer>
<ProfileLayoutContainer
singleOffer={props.singleOffer}
profile={props.profile}
>
{props.children}
<Grid container maxHeight maxWidth={1900}>
<MiddleCard item xs={9.5} lg={6.5} xl={6.6} md={6}>
<HeaderCard>
{props.headerCard}
</HeaderCard>
<Content>
{props.content}
</Content>
</MiddleCard>
<RightCard item xs={2.5} lg={3} xl={3} md={3}>
{props.rightCard}
<ContentRightCardContainer>
<Content item>{props.content}</Content>
<RightCard item singleOffer={props.singleOffer} profile={props.profile}>
{props.rightCard}
</RightCard>
</Grid>
</ContentRightCardContainer>
</ProfileLayoutContainer>
);
};
@@ -29,7 +29,8 @@ ProfileLayout.propTypes = {
leftCard: PropTypes.node,
content: PropTypes.node,
rightCard: PropTypes.node,
headerCard: PropTypes.node,
singleOffer: PropTypes.bool,
profile: PropTypes.bool,
};

export default ProfileLayout;

+ 45
- 32
src/layouts/ProfileLayout/ProfileLayout.styled.js Ver fichero

@@ -1,38 +1,51 @@
import { Box, Container, Grid } from "@mui/material";
import { Container, Grid, Box } from "@mui/material";
import styled from "styled-components";

export const ProfileLayoutContainer = styled(Container)`
padding-left: 0;
padding-right: 0;
margin: 0;
width: 100%;
display: flex;
max-width: none;
flex: 1;
height: 100%;
margin-top: 80px;
@media (max-width: 600px) {
margin-top: 40px;
}
`
padding-left: 36px;
padding-right: ${(props) => (props.singleOffer ? "76px" : 0)};
margin: 0;
width: 100%;
max-width: none;
/* display: flex; */
position: relative;
/* flex: 1; */
height: 100%;
@media (max-width: 1200px) {
padding-right: ${(props) => (props.profile ? 0 : "36px")};
}
@media (max-width: 600px) {
padding-left: 18px;
padding-right: ${(props) => (props.profile ? 0 : "18px")};
}
`;

export const LeftCard = styled(Grid)`
margin-top: 30px;
export const ContentRightCardContainer = styled(Box)`
display: flex;

@media screen and (max-width: 600px) {
flex-direction: column;
}
`;

export const RightCard = styled(Grid)`
margin-top: 0;
margin-left: 0;
padding-left: 0;
${(props) => props.profile && `min-width: 350px;`}

@media screen and (min-width: 600px) {
margin-top: 34px;
margin-left: ${(props) => (props.profile ? "0" : "36px")};
padding-left: ${(props) => (props.singleOffer ? "36px" : 0)};
border-top-right-radius: 4px;
background-color: green;
`
${(props) => props.singleOffer && `width: 100%`}
}

@media screen and (max-width: 1200px) {
margin-left: 0;
}
`;
export const Content = styled(Grid)`
background-color: yellow;
height: 100%;
`
export const RightCard = styled(Grid)`
margin-top: 30px;
border-top-left-radius: 4px;
background-color: blue;
`
export const MiddleCard = styled(Grid)`
`
export const HeaderCard = styled(Box)`
height: 450px;
background-color: orange;
`
width: 100%;
`;

+ 0
- 7
src/pages/HomePage/HomePageMUI.js Ver fichero

@@ -7,7 +7,6 @@ import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelect
import { useSelector } from "react-redux";
import { OFFERS_SCOPE } from "../../store/actions/offers/offersActionConstants";
import useOffers from "../../hooks/useOffers/useOffers";
import useSkeleton from "../../hooks/useSkeleton";

const HomePage = () => {
const isLoadingOffers = useSelector(
@@ -15,10 +14,6 @@ const HomePage = () => {
);
const [filtersOpened, setFiltersOpened] = useState(false);
const offers = useOffers();
const { transitionStage } = useSkeleton({
timeoutInterval: 900,
isLoadingIndicator: isLoadingOffers,
});
const toggleFilters = () => {
setFiltersOpened((prevFiltersOpened) => !prevFiltersOpened);
};
@@ -31,7 +26,6 @@ const HomePage = () => {
offers={offers}
filtersOpened={filtersOpened}
skeleton={isLoadingOffers}
animationStage={transitionStage}
toggleFilters={toggleFilters}
/>
}
@@ -39,7 +33,6 @@ const HomePage = () => {
<MarketPlace
offers={offers}
skeleton={isLoadingOffers}
animationStage={transitionStage}
toggleFilters={toggleFilters}
/>
}

+ 0
- 7
src/pages/MyOffers/MyOffers.js Ver fichero

@@ -5,7 +5,6 @@ import MainLayout from "../../layouts/MainLayout/MainLayout";
import FilterCard from "../../components/Cards/FilterCard/FilterCard";
import MarketPlace from "../../components/MarketPlace/MarketPlace";
import useMyOffers from "../../hooks/useOffers/useMyOffers";
import useSkeleton from "../../hooks/useSkeleton";
import { useSelector } from "react-redux";
import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors";
import { OFFERS_MINE_SCOPE } from "../../store/actions/offers/offersActionConstants";
@@ -17,10 +16,6 @@ const MyOffers = () => {
);
const [filtersOpened, setFiltersOpened] = useState(false);

const { transitionStage } = useSkeleton({
timeoutInterval: 900,
isLoadingIndicator: isLoadingMineOffers,
});
const toggleFilters = () => {
setFiltersOpened((prevFiltersOpened) => !prevFiltersOpened);
};
@@ -31,7 +26,6 @@ const MyOffers = () => {
<FilterCard
myOffers
offers={offers}
animationStage={transitionStage}
filtersOpened={filtersOpened}
toggleFilters={toggleFilters}
skeleton={isLoadingMineOffers}
@@ -41,7 +35,6 @@ const MyOffers = () => {
<MarketPlace
myOffers={true}
offers={offers}
animationStage={transitionStage}
skeleton={isLoadingMineOffers}
toggleFilters={toggleFilters}
/>

+ 6
- 2
src/pages/ProfilePage/ProfilePage.js Ver fichero

@@ -1,5 +1,5 @@
import React from "react";
import ItemDetailsLayout from "../../layouts/ItemDetailsLayout/ItemDetailsLayout";
import ProfileLayout from "../../layouts/ProfileLayout/ProfileLayout";
import { ProfilePageContainer } from "./ProfilePage.styled";
import Profile from "../../components/Profile/Profile";
import UserReviews from "../../components/UserReviews/UserReviews";
@@ -7,7 +7,11 @@ import UserReviews from "../../components/UserReviews/UserReviews";
const ProfilePage = () => {
return (
<ProfilePageContainer>
<ItemDetailsLayout content={<Profile/>} rightCard={<UserReviews isProfileReviews/>} profile />
<ProfileLayout
content={<Profile />}
rightCard={<UserReviews isProfileReviews />}
profile
/>
</ProfilePageContainer>
);
};

+ 11
- 18
src/store/reducers/login/loginReducer.js Ver fichero

@@ -1,4 +1,4 @@
import createReducer from '../../utils/createReducer';
import createReducer from "../../utils/createReducer";
import {
CLEAR_LOGIN_USER_ERROR,
LOGIN_USER_ERROR,
@@ -7,21 +7,18 @@ import {
UPDATE_USER_JWT_TOKEN,
GENERATE_TOKEN_SUCCESS,
GENERATE_TOKEN_ERROR,
} from '../../actions/login/loginActionConstants';
} from "../../actions/login/loginActionConstants";

const initialState = {
email: '',
token: {
RefreshToken: '',
JwtToken: '',
userId: ''
},
errorMessage: '',
email: "",
jwtToken: {},
refreshToken: {},
userId: "",
errorMessage: "",
};

export default createReducer(
{

[LOGIN_USER_SUCCESS]: setUser,
[UPDATE_USER_JWT_TOKEN]: setUserJwtToken,
[RESET_LOGIN_STATE]: resetLoginState,
@@ -30,24 +27,20 @@ export default createReducer(
[GENERATE_TOKEN_SUCCESS]: generateToken,
[GENERATE_TOKEN_ERROR]: generateTokenError,
},
initialState,
initialState
);


function setUser(state, action) {
return {
...state,
token: action.payload,
...action.payload,
};
}

function setUserJwtToken(state, action) {
return {
...state,
token: {
...state.token,
JwtToken: action.payload,
},
jwtToken: action.payload,
};
}

@@ -65,7 +58,7 @@ function resetLoginState() {
function clearLoginErrors(state) {
return {
...state,
errorMessage: '',
errorMessage: "",
};
}


+ 1
- 1
src/store/saga/loginSaga.js Ver fichero

@@ -59,7 +59,7 @@ function* fetchLogin({ payload }) {
yield call(addHeaderToken, token);
const profileData = yield call(attemptFetchProfile, userId);
if (profileData) yield put(setMineProfile(profileData.data));
yield put(fetchUserSuccess({JwtToken: accessToken, RefreshToken: refreshToken, userId}));
yield put(fetchUserSuccess({jwtToken: accessToken, refreshToken: refreshToken, userId}));
if (payload.handleApiResponseSuccess) {
yield call(payload.handleApiResponseSuccess);
}

+ 2
- 2
src/store/saga/registerSaga.js Ver fichero

@@ -60,8 +60,8 @@ function* fetchRegisterUser({ payload }) {
if (profileData) yield put(setMineProfile(profileData.data));
yield put(
fetchUserSuccess({
JwtToken: accessToken,
RefreshToken: refreshToken,
jwtToken: accessToken,
refreshToken: refreshToken,
userId,
})
);

+ 4
- 24
src/store/selectors/loginSelectors.js Ver fichero

@@ -1,31 +1,11 @@
import { createSelector } from 'reselect';
import { createSelector } from "reselect";

const loginSelector = (state) => state.login;

export const selectLoginEmail = createSelector(
loginSelector,
(state) => state.email,
);

export const selectUsernames = createSelector(
loginSelector,
(state) => state.usernames,
);

export const selectTokens = createSelector(
loginSelector,
(state) => state.token,
);

export const selectJWTToken = createSelector(
loginSelector,
(state) => state.token,
);
export const selectUserId = createSelector(
loginSelector,
(state) => state.token.userId
)
(state) => state.userId
);
export const selectLoginError = createSelector(
loginSelector,
(state) => state.errorMessage,
(state) => state.errorMessage
);

+ 21
- 0
src/util/helpers/routeHelpers.js Ver fichero

@@ -1,3 +1,11 @@
import {
FORGOT_PASSWORD_MAIL_SENT,
FORGOT_PASSWORD_PAGE,
LOGIN_PAGE,
REGISTER_PAGE,
REGISTER_SUCCESSFUL_PAGE,
RESET_PASSWORD_PAGE,
} from "../../constants/pages";
import history from "../../store/utils/history";

export const routeMatches = (route) => {
@@ -18,3 +26,16 @@ export const replaceInRoute = (route, pathVariables = {}) => {
route
);
};
export const isAuthRoute = () => {
if (
routeMatches(LOGIN_PAGE) ||
routeMatches(REGISTER_PAGE) ||
routeMatches(REGISTER_SUCCESSFUL_PAGE) ||
routeMatches(FORGOT_PASSWORD_PAGE) ||
routeMatches(FORGOT_PASSWORD_MAIL_SENT) ||
routeMatches(RESET_PASSWORD_PAGE)
) {
return true;
}
return false;
};

Cargando…
Cancelar
Guardar