Djordje Mitrovic hace 3 años
padre
commit
9a42dfb9ec
Se han modificado 47 ficheros con 1222 adiciones y 24986 borrados
  1. 119
    24800
      package-lock.json
  2. 2
    0
      package.json
  3. 6
    0
      public/index.html
  4. 8
    1
      src/AppRoutes.js
  5. 8
    0
      src/assets/images/svg/bulleted-list.svg
  6. 95
    0
      src/components/About/AboutPageContent.js
  7. 12
    0
      src/components/About/AboutPageContent.styled.js
  8. 2
    2
      src/components/About/CheckOffersButton/CheckOffersButton.js
  9. 2
    2
      src/components/Cards/CreateOfferCard/CreateOffer.styled.js
  10. 8
    31
      src/components/Cards/CreateOfferCard/FirstPart/OfferDescriptionField/OfferDescriptionField.js
  11. 15
    0
      src/components/Cards/CreateOfferCard/FirstPart/OfferDescriptionField/OfferDescriptionField.styled.js
  12. 4
    1
      src/components/Cards/CreateOfferCard/FirstPart/OfferTitleField/OfferTitleField.styled.js
  13. 25
    0
      src/components/Cards/ItemDetailsCard/OfferDetails/OfferDescription/OfferDescription.js
  14. 13
    0
      src/components/Cards/ItemDetailsCard/OfferDetails/OfferDescription/OfferDescription.styled.js
  15. 2
    4
      src/components/Cards/ItemDetailsCard/OfferDetails/OfferDetails.js
  16. 0
    7
      src/components/Cards/ItemDetailsCard/OfferDetails/OfferDetails.styled.js
  17. 9
    1
      src/components/Cards/OfferCard/OfferDescription/OfferDescription.js
  18. 1
    1
      src/components/Footer/AboutFooter.styled.js
  19. 24
    8
      src/components/Header/Header.js
  20. 8
    3
      src/components/Header/SearchInput/SearchInput.js
  21. 54
    0
      src/components/Popovers/ColorPicker/ColorPicker.js
  22. 43
    0
      src/components/Popovers/ColorPicker/ColorPicker.styled.js
  23. 79
    0
      src/components/RichTextComponent/BlockButton/BlockButton.js
  24. 27
    0
      src/components/RichTextComponent/BlockButton/BlockButton.styled.js
  25. 58
    0
      src/components/RichTextComponent/ColorButton/ColorButton.js
  26. 29
    0
      src/components/RichTextComponent/ColorButton/ColorButton.styled.js
  27. 26
    0
      src/components/RichTextComponent/ColorIcon/ColorIcon.js
  28. 15
    0
      src/components/RichTextComponent/ColorIcon/ColorIcon.styled.js
  29. 43
    0
      src/components/RichTextComponent/MarkButton/MarkButton.js
  30. 27
    0
      src/components/RichTextComponent/MarkButton/MarkButton.styled.js
  31. 114
    0
      src/components/RichTextComponent/RichTextComponent.js
  32. 76
    0
      src/components/RichTextComponent/RichTextComponent.styled.js
  33. 70
    0
      src/components/RichTextComponent/RichTextElement/RichTextElement.js
  34. 10
    0
      src/components/RichTextComponent/RichTextElement/RichTextElement.styled.js
  35. 31
    0
      src/components/RichTextComponent/RichTextLeaf/RichTextLeaf.js
  36. 4
    1
      src/components/TextFields/TextField/TextField.js
  37. 1
    0
      src/constants/pages.js
  38. 12
    0
      src/hooks/useIsLoggedIn.js
  39. 8
    0
      src/i18n/resources/rs.js
  40. 3
    86
      src/pages/About/AboutPage.js
  41. 0
    12
      src/pages/About/AboutPage.styled.js
  42. 33
    22
      src/pages/HomePage/HomePage.js
  43. 8
    0
      src/pages/Marketplace/MarketplacePage.js
  44. 3
    1
      src/store/saga/offersSaga.js
  45. 9
    0
      src/themes/primaryTheme/primaryThemeColors.js
  46. 8
    0
      src/util/helpers/jsonHelper.js
  47. 68
    3
      yarn.lock

+ 119
- 24800
package-lock.json
La diferencia del archivo ha sido suprimido porque es demasiado grande
Ver fichero


+ 2
- 0
package.json Ver fichero

@@ -45,6 +45,8 @@
"redux-saga": "^1.1.3",
"rive-react": "^3.0.23",
"sass": "^1.34.1",
"slate": "^0.86.0",
"slate-react": "^0.86.0",
"socket.io": "^4.5.1",
"socket.io-client": "^4.5.1",
"styled-components": "^5.3.5",

+ 6
- 0
public/index.html Ver fichero

@@ -14,6 +14,12 @@
href="https://fonts.googleapis.com/css?family=Poppins&display=swap"
onload="this.onload=null;this.rel='stylesheet';this.type='text/css'"
/>
<link
rel="preload"
as="style"
href="https://fonts.googleapis.com/css?family=Source+Code+Pro:400,700&display=swap"
onload="this.onload=null;this.rel='stylesheet';this.type='text/css'"
/>
<link
style
rel="preload"

+ 8
- 1
src/AppRoutes.js Ver fichero

@@ -20,6 +20,7 @@ import {
ADMIN_HOME_PAGE,
MESSAGES_LIST_PAGE,
DIRECT_CHAT_PAGE,
MARKETPLACE_PAGE,
} from "./constants/pages";
import LoginPage from "./pages/LoginPage/LoginPage";
import AdminLoginPage from "./pages/AdminLoginPage/AdminLoginPage";
@@ -42,6 +43,7 @@ import AboutPage from "./pages/About/AboutPage";
import AuthRoute from "./components/Router/AuthRoute";
import AdminRoute from "./components/Router/AdminRoute";
import AdminHomePage from "./pages/AdminHomePage/AdminHomePage";
import MarketplacePage from "./pages/Marketplace/MarketplacePage";

const AppRoutes = () => {
return (
@@ -54,12 +56,17 @@ const AppRoutes = () => {
<AuthRoute path={FORGOT_PASSWORD_PAGE} component={ForgotPasswordPage} />
<AuthRoute path={RESET_PASSWORD_PAGE} component={ResetPasswordPage} />
<Route path={REGISTER_SUCCESSFUL_PAGE} component={RegisterSuccessful} />
<Route path={MARKETPLACE_PAGE} component={MarketplacePage} />
<Route path={CREATE_OFFER_PAGE} component={CreateOffer} />
<Route path={ITEM_DETAILS_PAGE} component={ItemDetailsPage} />
<Route path={PROFILE_PAGE} component={ProfilePage} />
<Route path={ABOUT_PAGE} component={AboutPage} />
<Route path={HOME_PAGE} component={HomePage} />
<PrivateRoute exact path={MESSAGES_LIST_PAGE} component={MessagesListPage} />
<PrivateRoute
exact
path={MESSAGES_LIST_PAGE}
component={MessagesListPage}
/>
<PrivateRoute path={DIRECT_CHAT_PAGE} component={DirectChatPage} />
<PrivateRoute path={MY_OFFERS_PAGE} component={MyOffers} />
<AdminRoute path={ADMIN_HOME_PAGE} component={AdminHomePage} />

+ 8
- 0
src/assets/images/svg/bulleted-list.svg Ver fichero

@@ -0,0 +1,8 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 4.5H15.75" stroke="#667080" stroke-width="1.24" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 9H15.75" stroke="#667080" stroke-width="1.24" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 13.5H15.75" stroke="#667080" stroke-width="1.24" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.25 4.5H2.2575" stroke="#667080" stroke-width="1.24" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.25 9H2.2575" stroke="#667080" stroke-width="1.24" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.25 13.5H2.2575" stroke="#667080" stroke-width="1.24" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

+ 95
- 0
src/components/About/AboutPageContent.js Ver fichero

@@ -0,0 +1,95 @@
import React, { useEffect, useRef } from "react";
import PropTypes from "prop-types";
import { useLocation } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { selectAboutRouteSelected } from "../../store/selectors/appSelectors";
import scrollConstants from "../../constants/scrollConstants";
import { setAboutRouteSelected } from "../../store/actions/app/appActions";
import { AboutPageContainer } from "./AboutPageContent.styled";
import AboutComponent from "./AboutComponent";
import PricesComponent from "../Prices/PricesComponent";
import PrivacyPolicyComponent from "../PrivacyPolicy/PrivacyPolicyComponent";
import AboutFooter from "../Footer/AboutFooter";

const AboutPageContent = () => {
const aboutRef = useRef(null);
const pricesRef = useRef(null);
const privacyPolicyRef = useRef(null);
const dispatch = useDispatch();
const aboutRouteSelected = useSelector(selectAboutRouteSelected);
const location = useLocation();
useEffect(() => {
if (location.state && location.state?.clicked) {
if (location.state.navigation === scrollConstants.about.aboutPage) {
window.scrollTo({ top: 0, behavior: "smooth" });
if (aboutRouteSelected !== scrollConstants.about.aboutPage) {
dispatch(setAboutRouteSelected(scrollConstants.about.aboutPage));
}
}
if (location.state.navigation === scrollConstants.about.pricesPage) {
const yAxis = pricesRef.current.offsetTop;
window.scrollTo({ top: yAxis, behavior: "smooth" });
if (aboutRouteSelected !== scrollConstants.about.pricesPage) {
dispatch(setAboutRouteSelected(scrollConstants.about.pricesPage));
}
}
if (
location.state.navigation === scrollConstants.about.privacyPolicyPage
) {
const yAxis = privacyPolicyRef.current.offsetTop - 64;
window.scrollTo({ top: yAxis, behavior: "smooth" });
if (aboutRouteSelected !== scrollConstants.about.privacyPolicyPage) {
dispatch(
setAboutRouteSelected(scrollConstants.about.privacyPolicyPage)
);
}
}
location.state = {};
}
}, [location]);

useEffect(() => {
const listener = () => {
if (
window.scrollY >
pricesRef.current.offsetTop - window.innerHeight / 2
) {
if (
window.scrollY >
privacyPolicyRef.current.offsetTop - window.innerHeight / 2
) {
if (aboutRouteSelected !== scrollConstants.about.privacyPolicyPage) {
dispatch(
setAboutRouteSelected(scrollConstants.about.privacyPolicyPage)
);
}
} else if (aboutRouteSelected !== scrollConstants.about.pricesPage) {
dispatch(setAboutRouteSelected(scrollConstants.about.pricesPage));
}
} else {
if (aboutRouteSelected !== scrollConstants.about.aboutPage) {
dispatch(setAboutRouteSelected(scrollConstants.about.aboutPage));
}
}
};
window.addEventListener("scroll", listener);
return () => window.removeEventListener("scroll", listener);
}, [aboutRouteSelected]);
return (
<AboutPageContainer>
<AboutComponent ref={aboutRef} id={scrollConstants.about.aboutPage} />
<PricesComponent ref={pricesRef} id={scrollConstants.about.pricesPage} />
<PrivacyPolicyComponent
ref={privacyPolicyRef}
id={scrollConstants.about.privacyPolicyPage}
/>
<AboutFooter />
</AboutPageContainer>
);
};

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

export default AboutPageContent;

+ 12
- 0
src/components/About/AboutPageContent.styled.js Ver fichero

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

export const AboutPageContainer = styled(Box)`
margin-top: 64px;
background-color: ${selectedTheme.colors.staticBackgroundColor};
position: relative;
@media (max-width: 600px) {
margin-top: 53px;
}
`

+ 2
- 2
src/components/About/CheckOffersButton/CheckOffersButton.js Ver fichero

@@ -3,13 +3,13 @@ import PropTypes from "prop-types";
import { CheckOffersButtonContainer } from "./CheckOffersButton.styled";
import selectedTheme from "../../../themes";
import { useTranslation } from "react-i18next";
import { HOME_PAGE } from "../../../constants/pages";
import { MARKETPLACE_PAGE } from "../../../constants/pages";
import history from "../../../store/utils/history";

const CheckOffersButton = () => {
const { t } = useTranslation();
const handleClick = () => {
history.push(HOME_PAGE);
history.push(MARKETPLACE_PAGE);
};
return (
<CheckOffersButtonContainer

+ 2
- 2
src/components/Cards/CreateOfferCard/CreateOffer.styled.js Ver fichero

@@ -231,8 +231,8 @@ export const NextButtonContainer = styled(PrimaryButton)`
}

@media screen and (max-width: 600px) {
position: absolute;
bottom: 18px;
/* position: absolute; */
/* bottom: 18px; */
height: 44px;
width: 339px;
/* left: 9px; */

+ 8
- 31
src/components/Cards/CreateOfferCard/FirstPart/OfferDescriptionField/OfferDescriptionField.js Ver fichero

@@ -1,45 +1,22 @@
import React from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
import useIsMobile from "../../../../../hooks/useIsMobile";
import { FieldLabel } from "../FirstPartCreateOffer.styled";
import { DescriptionField } from "./OfferDescriptionField.styled";
import RichTextComponent from "../../../../RichTextComponent/RichTextComponent";

const OfferDescriptionField = (props) => {
const formik = props.formik;
const { t } = useTranslation();
const { isMobile } = useIsMobile();
const handleChange = (newValue) => {
formik.setFieldValue("description", newValue);
};
return (
<>
<FieldLabel leftText={t("offer.productDescription")} />
{!isMobile ? (
<DescriptionField
name="description"
placeholder={t("offer.description")}
margin="normal"
italicPlaceholder
value={formik.values.description}
onChange={formik.handleChange}
error={formik.touched.description && formik.errors.description}
helperText={formik.touched.description && formik.errors.description}
fullWidth
multiline
minRows={4}
height={"100px"}
/>
) : (
<DescriptionField
name="description"
placeholder={t("offer.description")}
margin="normal"
italicPlaceholder
value={formik.values.description}
onChange={formik.handleChange}
error={formik.touched.description && formik.errors.description}
helperText={formik.touched.description && formik.errors.description}
fullWidth
/>
)}
<RichTextComponent
onChange={handleChange}
value={formik.values.description}
/>
</>
);
};

+ 15
- 0
src/components/Cards/CreateOfferCard/FirstPart/OfferDescriptionField/OfferDescriptionField.styled.js Ver fichero

@@ -1,4 +1,5 @@
import styled from "styled-components";
import selectedTheme from "../../../../../themes";
import { TextField } from "../../../../TextFields/TextField/TextField";

export const DescriptionField = styled(TextField)`
@@ -13,3 +14,17 @@ export const DescriptionField = styled(TextField)`
}
}
`;
export const Element = styled.p`
background-color: ${selectedTheme.colors.primaryBackgroundColor};
padding: 0;
box-sizing: border-box;
overflow-y: auto;
width: 100%;
margin: 16px 0;
padding-left: 0;
height: 100px;
font-size: 16px;
padding: 8px 14px;
border: 1px solid rgba(0, 0, 0, 0.23);
border-radius: 4px;
`;

+ 4
- 1
src/components/Cards/CreateOfferCard/FirstPart/OfferTitleField/OfferTitleField.styled.js Ver fichero

@@ -2,6 +2,9 @@ import styled from "styled-components";
import { TextField } from "../../../../TextFields/TextField/TextField";

export const TitleField = styled(TextField)`
& input {
background-color: white;
}
@media (max-width: 600px) {
margin-bottom: 0;
& div div input {
@@ -11,4 +14,4 @@ export const TitleField = styled(TextField)`
height: 40px;
}
}
`;
`;

+ 25
- 0
src/components/Cards/ItemDetailsCard/OfferDetails/OfferDescription/OfferDescription.js Ver fichero

@@ -0,0 +1,25 @@
import React from "react";
import PropTypes from "prop-types";
import { EditableContainer } from "./OfferDescription.styled";
import RichTextComponent from "../../../../RichTextComponent/RichTextComponent";
import { isJsonString } from "../../../../../util/helpers/jsonHelper";
import { useMemo } from "react";

const OfferDescription = (props) => {
const description = useMemo(
() =>
isJsonString(props?.value) ? (
<RichTextComponent itemDetails value={props?.value || ""} readOnly />
) : (
<></>
),
[props?.value]
);
return <EditableContainer>{description}</EditableContainer>;
};

OfferDescription.propTypes = {
value: PropTypes.string,
};

export default OfferDescription;

+ 13
- 0
src/components/Cards/ItemDetailsCard/OfferDetails/OfferDescription/OfferDescription.styled.js Ver fichero

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

export const EditableContainer = styled(Box)`
font-size: 16px;
line-height: 22px;
white-space: pre-line;
max-width: 100%;
@media (max-width: 600px) {
font-size: 14px;
max-width: 100%;
}
`;

+ 2
- 4
src/components/Cards/ItemDetailsCard/OfferDetails/OfferDetails.js Ver fichero

@@ -2,7 +2,6 @@ import React from "react";
import PropTypes from "prop-types";
import {
Details,
OfferDescriptionText,
OfferDescriptionTitle,
OfferImage,
OfferLittleDetails,
@@ -19,6 +18,7 @@ import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter";
import { useEffect } from "react";
import { useState } from "react";
import ImagesCarousel from "../ImagesCarousel/ImagesCarousel";
import OfferDescription from "./OfferDescription/OfferDescription";

const OfferDetails = (props) => {
const offer = props.offer;
@@ -110,9 +110,7 @@ const OfferDetails = (props) => {
<OfferDescriptionTitle>
{t("itemDetailsCard.description")}
</OfferDescriptionTitle>
<OfferDescriptionText showBarterButton={props.showExchangeButton}>
{offer?.description}
</OfferDescriptionText>
<OfferDescription value={offer?.description} />
<DesciprtionPostDate previewCard={props.previewCard}>
{date}
</DesciprtionPostDate>

+ 0
- 7
src/components/Cards/ItemDetailsCard/OfferDetails/OfferDetails.styled.js Ver fichero

@@ -135,17 +135,10 @@ export const OfferDescriptionText = styled(Box)`
line-height: 22px;
white-space: pre-line;
max-width: 100%;
/* max-width: ${(props) =>
props.showBarterButton ? "calc(100% - 230px)" : "100%"}; */
@media (max-width: 600px) {
font-size: 14px;
max-width: 100%;
}
/* max-width: calc(100% - 230px); */
/* overflow: hidden; */
/* display: -webkit-box;
-webkit-line-clamp: 5;
-webkit-box-orient: vertical; */
`;
export const DesciprtionPostDate = styled(Typography)`
display: none;

+ 9
- 1
src/components/Cards/OfferCard/OfferDescription/OfferDescription.js Ver fichero

@@ -6,6 +6,8 @@ import {
OfferDescriptionTitle,
} from "./OfferDescription.styled";
import { useTranslation } from "react-i18next";
import RichTextComponent from "../../../RichTextComponent/RichTextComponent";
import { isJsonString } from "../../../../util/helpers/jsonHelper";

const OfferDescription = (props) => {
const { t } = useTranslation();
@@ -14,7 +16,13 @@ const OfferDescription = (props) => {
<OfferDescriptionTitle>
{t("offer.descriptionLabel")}
</OfferDescriptionTitle>
<OfferDescriptionText>{props.description}</OfferDescriptionText>
<OfferDescriptionText>
{isJsonString(props?.description) ? (
<RichTextComponent readOnly offerCard value={props?.description} />
) : (
""
)}
</OfferDescriptionText>
</OfferDescriptionContainer>
);
};

+ 1
- 1
src/components/Footer/AboutFooter.styled.js Ver fichero

@@ -67,5 +67,5 @@ export const LinkText = styled(Typography)`
`;

export const Arrow = styled(ArrowButton)`
transform: rotate(-45deg);
transform: rotate(-90deg);
`;

+ 24
- 8
src/components/Header/Header.js Ver fichero

@@ -21,6 +21,7 @@ import {
ADMIN_HOME_PAGE,
BASE_PAGE,
HOME_PAGE,
MARKETPLACE_PAGE,
} from "../../constants/pages";
import { fetchMineProfile } from "../../store/actions/profile/profileActions";
import useSearch from "../../hooks/useOffers/useSearch";
@@ -106,12 +107,21 @@ const Header = () => {
},
});
} else {
history.push({
pathname: HOME_PAGE,
state: {
logo: true,
},
});
if (user) {
history.push({
pathname: HOME_PAGE,
state: {
logo: true,
},
});
} else {
history.push({
pathname: MARKETPLACE_PAGE,
state: {
logo: true,
},
});
}
if (searchRef?.current) searchRef.current.value = "";
}
};
@@ -143,7 +153,11 @@ const Header = () => {
handleSearch={handleSearch}
user={user}
/>
{routeMatches(ABOUT_PAGE) && <AboutHeader />}
{(routeMatches(ABOUT_PAGE) ||
(!user &&
(routeMatches(HOME_PAGE) || routeMatches(BASE_PAGE)))) && (
<AboutHeader />
)}

{user ? (
<ToolsButtonsContainer
@@ -177,7 +191,9 @@ const Header = () => {
/>
) : (
<React.Fragment>
{routeMatches(ABOUT_PAGE) ? (
{routeMatches(ABOUT_PAGE) ||
routeMatches(HOME_PAGE) ||
routeMatches(BASE_PAGE) ? (
<MarketplaceLinkRouteContainer>
<MarketplaceLinkRoute onClick={() => handleLogoClick()}>
{t("admin.navigation.marketplace")}

+ 8
- 3
src/components/Header/SearchInput/SearchInput.js Ver fichero

@@ -11,16 +11,18 @@ import { useTranslation } from "react-i18next";
import { routeMatches } from "../../../util/helpers/routeHelpers";
import { ABOUT_PAGE, BASE_PAGE, HOME_PAGE } from "../../../constants/pages";
import { debounceHelper } from "../../../util/helpers/debounceHelper";
import useIsLoggedIn from "../../../hooks/useIsLoggedIn";

const SearchInput = forwardRef((props, ref) => {
const { t } = useTranslation();
const { isLoggedIn } = useIsLoggedIn();
const handleSearch = () => {
if (routeMatches(HOME_PAGE) || routeMatches(BASE_PAGE)) {
console.log("uslo unutra")
console.log("uslo unutra");
debounceHelper(() => props.handleSearch(ref.current.value), 500);
}
};
console.log(routeMatches(HOME_PAGE))
console.log(routeMatches(HOME_PAGE));
const handleManualSearch = () => {
debounceHelper(() => {}, 500);
props.handleSearch(ref.current.value);
@@ -40,7 +42,10 @@ const SearchInput = forwardRef((props, ref) => {
const handleBlurSearch = () => {
ref.current.removeEventListener("keyup", listener);
};
if (routeMatches(ABOUT_PAGE)) {
if (
routeMatches(ABOUT_PAGE) ||
(!isLoggedIn && (routeMatches(HOME_PAGE) || routeMatches(BASE_PAGE)))
) {
return <></>;
}
return (

+ 54
- 0
src/components/Popovers/ColorPicker/ColorPicker.js Ver fichero

@@ -0,0 +1,54 @@
import React from "react";
import PropTypes from "prop-types";
import {
ColorPickerContainer,
ColorPickerLabel,
ColorPickerName,
ColorPickerNameContainer,
ColorsContainer,
} from "./ColorPicker.styled";
import { useTranslation } from "react-i18next";
import selectedTheme from "../../../themes";
import ColorIcon from "../../RichTextComponent/ColorIcon/ColorIcon";

const ColorPicker = (props) => {
const { t } = useTranslation();
const handleClickColor = (singleColor) => {
props?.handleClickColor({
color: selectedTheme.colors.colorPicker[singleColor],
colorName: singleColor,
});
};
return (
<ColorPickerContainer>
<ColorPickerNameContainer>
<ColorPickerLabel>{t("colorPicker.label")}</ColorPickerLabel>
<ColorPickerName>
{t(`colorPicker.${props.selectedColorName}`)}
</ColorPickerName>
</ColorPickerNameContainer>
<ColorsContainer>
{Object.keys(selectedTheme.colors.colorPicker).map((singleColor) => {
if (singleColor === "border") return;
return (
<ColorIcon
selected={singleColor === props?.selectedColorName}
selectedInPopover={singleColor === props?.selectedColorName}
color={selectedTheme.colors.colorPicker[singleColor]}
key={singleColor}
onClick={() => handleClickColor(singleColor)}
/>
);
})}
</ColorsContainer>
</ColorPickerContainer>
);
};

ColorPicker.propTypes = {
selectedColorName: PropTypes.string,
selectedColor: PropTypes.string,
handleClickColor: PropTypes.func,
};

export default ColorPicker;

+ 43
- 0
src/components/Popovers/ColorPicker/ColorPicker.styled.js Ver fichero

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

export const ColorPickerContainer = styled(Box)`
padding: 9px 18px;
isolation: isolate;
width: 178px;
height: 74px;
background: white;
box-shadow: 4px 4px 9px rgba(0, 0, 0, 0.12);
border-radius: 4px;
display: flex;
flex-direction: column;
gap: 9px;
z-index: 1000;
`;
export const ColorPickerNameContainer = styled(Box)`
display: flex;
flex-direction: row;
gap: 3px;
align-items: flex-end;
`;
export const ColorPickerLabel = styled(Typography)`
color: ${selectedTheme.colors.primaryTextDisabled};
font-family: ${selectedTheme.fonts.textFont};
font-size: 12px;
line-height: 15.5px;
letter-spacing: 1px;
`;
export const ColorPickerName = styled(Typography)`
color: ${selectedTheme.colors.primaryText};
font-family: ${selectedTheme.fonts.textFont};
font-size: 16px;
line-height: 18.5px;
`;
export const ColorsContainer = styled(Box)`
display: flex;
flex-direction: row;
gap: 14px;
padding: 4px;
align-items: flex-end;
`;

+ 79
- 0
src/components/RichTextComponent/BlockButton/BlockButton.js Ver fichero

@@ -0,0 +1,79 @@
import React from "react";
import PropTypes from "prop-types";
import { useSlate } from "slate-react";
import { Editor, Transforms, Element as SlateElement } from "slate";
import { BlockButtonContainer, BlockButtonIcon } from "./BlockButton.styled";
const LIST_TYPES = ["numbered-list", "bulleted-list", "link"];
const TEXT_ALIGN_TYPES = ["left", "center", "right", "justify"];
const BlockButton = ({ format, icon }) => {
const editor = useSlate();
return (
<BlockButtonContainer
active={isBlockActive(
editor,
format,
TEXT_ALIGN_TYPES.includes(format) ? "align" : "type"
)}
onMouseDown={(event) => {
event.preventDefault();
toggleBlock(editor, format);
}}
>
<BlockButtonIcon>{icon}</BlockButtonIcon>
</BlockButtonContainer>
);
};
const isBlockActive = (editor, format, blockType = "type") => {
const { selection } = editor;
if (!selection) return false;

const [match] = Array.from(
Editor.nodes(editor, {
at: Editor.unhangRange(editor, selection),
match: (n) =>
!Editor.isEditor(n) &&
SlateElement.isElement(n) &&
n[blockType] === format,
})
);

return !!match;
};
const toggleBlock = (editor, format) => {
const isActive = isBlockActive(
editor,
format,
TEXT_ALIGN_TYPES.includes(format) ? "align" : "type"
);
const isList = LIST_TYPES.includes(format);

Transforms.unwrapNodes(editor, {
match: (n) =>
!Editor.isEditor(n) &&
SlateElement.isElement(n) &&
LIST_TYPES.includes(n.type) &&
!TEXT_ALIGN_TYPES.includes(format),
split: true,
});
let newProperties;
if (TEXT_ALIGN_TYPES.includes(format)) {
newProperties = {
align: isActive ? undefined : format,
};
} else {
newProperties = {
type: isActive ? "paragraph" : isList ? "list-item" : format,
};
}
Transforms.setNodes < SlateElement > (editor, newProperties);

if (!isActive && isList) {
const block = { type: format, children: [] };
Transforms.wrapNodes(editor, block);
}
};
BlockButton.propTypes = {
format: PropTypes.any,
icon: PropTypes.any,
};
export default BlockButton;

+ 27
- 0
src/components/RichTextComponent/BlockButton/BlockButton.styled.js Ver fichero

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

export const BlockButtonContainer = styled(Box)`
display: inline;
cursor: pointer;
`;
export const BlockButtonIcon = styled(Box)`
font-size: 16px;
font-family: "Source Code Pro";
${(props) =>
props.format === "bold"
? `font-weight: bold;`
: props.format === "italic"
? "font-style: italic;"
: props.format === "underline"
? `text-decoration: underline;`
: ""}
color: ${selectedTheme.colors.primaryGrayText};
line-height: 20px;
${(props) =>
props.active &&
css`
color: ${selectedTheme.colors.primaryText};
`}
`;

+ 58
- 0
src/components/RichTextComponent/ColorButton/ColorButton.js Ver fichero

@@ -0,0 +1,58 @@
import React from "react";
import PropTypes from "prop-types";
import { useSlate } from "slate-react";
import { Editor } from "slate";
import { ColorButtonContainer, ColorButtonIcon } from "./ColorButton.styled";
import { useState } from "react";
import ColorIcon from "../ColorIcon/ColorIcon";
import ColorPicker from "../../Popovers/ColorPicker/ColorPicker";

// const colorMarks = ["darkGray", "gray", "purple", "pink", "yellow"];

const toggleMark = (editor, format, newColor) => {
Editor.addMark(editor, format, newColor);
};

// const isMarkActive = (editor, format) => {
// const marks = Editor.marks(editor);
// return marks ? marks[format] === true : false;
// };

const ColorButton = (props) => {
const editor = useSlate();
const [isColorPickerPopoverShowing, setIsColorPickerPopoverShowing] =
useState(false);

const callbackFunction = (newColor) => {
setIsColorPickerPopoverShowing(false);
props?.setSelectedColor(newColor);
toggleMark(editor, "color", newColor.color);
};
console.log();
return (
<>
<ColorButtonContainer
format={props?.format}
onMouseDown={() => setIsColorPickerPopoverShowing(true)}
>
<ColorButtonIcon format={props?.format}>
<ColorIcon selected color={props?.selectedColor.color} />
</ColorButtonIcon>
</ColorButtonContainer>
{isColorPickerPopoverShowing && (
<ColorPicker
selectedColor={props?.selectedColor.color}
selectedColorName={props?.selectedColor.colorName}
handleClickColor={callbackFunction}
/>
)}
</>
);
};
ColorButton.propTypes = {
format: PropTypes.any,
icon: PropTypes.any,
selectedColor: PropTypes.any,
setSelectedColor: PropTypes.func,
};
export default ColorButton;

+ 29
- 0
src/components/RichTextComponent/ColorButton/ColorButton.styled.js Ver fichero

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

export const ColorButtonContainer = styled(Box)`
display: inline;
cursor: pointer;
position: relative;
top: 2px;
`;
export const ColorButtonIcon = styled(Box)`
font-size: 16px;
font-family: "Source Code Pro";
${(props) =>
props.format === "bold"
? `font-weight: bold;`
: props.format === "italic"
? "font-style: italic;"
: props.format === "underline"
? `text-decoration: underline;`
: ""}
color: ${selectedTheme.colors.primaryGrayText};
line-height: 20px;
${(props) =>
props.active &&
css`
color: ${selectedTheme.colors.primaryText};
`}
`;

+ 26
- 0
src/components/RichTextComponent/ColorIcon/ColorIcon.js Ver fichero

@@ -0,0 +1,26 @@
import React from "react";
import PropTypes from "prop-types";
import { ColorIconContainer } from "./ColorIcon.styled";

const ColorIcon = (props) => {
return (
<ColorIconContainer
selected={props?.selected}
backgroundColor={props?.color}
aboveEditor={props?.aboveEditor}
selectedInPopover={props?.selectedInPopover}
onClick={props?.onClick}
/>
);
};

ColorIcon.propTypes = {
children: PropTypes.node,
color: PropTypes.string,
selected: PropTypes.bool,
aboveEditor: PropTypes.bool,
selectedInPopover: PropTypes.bool,
onClick: PropTypes.func,
};

export default ColorIcon;

+ 15
- 0
src/components/RichTextComponent/ColorIcon/ColorIcon.styled.js Ver fichero

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

export const ColorIconContainer = styled(Box)`
cursor: pointer;
width: ${(props) =>
props.aboveEditor ? "13.5px" : props.selectedInPopover ? "17px" : "15px"};
height: ${(props) =>
props.aboveEditor ? "13.5px" : props.selectedInPopover ? "17px" : "15px"};
background-color: ${(props) => props.backgroundColor};
border: 1.24px solid
${(props) =>
props.selected ? selectedTheme.colors.colorPicker.border : "transparent"};
`;

+ 43
- 0
src/components/RichTextComponent/MarkButton/MarkButton.js Ver fichero

@@ -0,0 +1,43 @@
import React from "react";
import PropTypes from "prop-types";
import { useSlate } from "slate-react";
import { Editor } from "slate";
import { MarkButtonContainer, MarkButtonIcon } from "./MarkButton.styled";

const toggleMark = (editor, format) => {
const isActive = isMarkActive(editor, format);

if (isActive) {
Editor.removeMark(editor, format);
} else {
Editor.addMark(editor, format, true);
}
};

const isMarkActive = (editor, format) => {
const marks = Editor.marks(editor);
return marks ? marks[format] === true : false;
};

const MarkButton = ({ format, icon }) => {
const editor = useSlate();
return (
<MarkButtonContainer
active={isMarkActive(editor, format)}
format={format}
onMouseDown={(event) => {
event.preventDefault();
toggleMark(editor, format);
}}
>
<MarkButtonIcon format={format} active={isMarkActive(editor, format)}>
{icon}
</MarkButtonIcon>
</MarkButtonContainer>
);
};
MarkButton.propTypes = {
format: PropTypes.any,
icon: PropTypes.any,
};
export default MarkButton;

+ 27
- 0
src/components/RichTextComponent/MarkButton/MarkButton.styled.js Ver fichero

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

export const MarkButtonContainer = styled(Box)`
display: inline;
cursor: pointer;
`;
export const MarkButtonIcon = styled(Box)`
font-size: 16px;
font-family: "Source Code Pro";
${(props) =>
props.format === "bold"
? `font-weight: bold;`
: props.format === "italic"
? "font-style: italic;"
: props.format === "underline"
? `text-decoration: underline;`
: ""}
color: ${selectedTheme.colors.primaryGrayText};
line-height: 20px;
${(props) =>
props.active &&
css`
color: ${selectedTheme.colors.primaryText};
`}
`;

+ 114
- 0
src/components/RichTextComponent/RichTextComponent.js Ver fichero

@@ -0,0 +1,114 @@
import React, { useCallback, useMemo } from "react";
import { Editable, withReact, Slate } from "slate-react";
import { createEditor } from "slate";
import PropTypes from "prop-types";

import RichTextElement from "./RichTextElement/RichTextElement";
import MarkButton from "./MarkButton/MarkButton";
import RichTextLeaf from "./RichTextLeaf/RichTextLeaf";
import { ReactComponent as BulletedList } from "../../assets/images/svg/bulleted-list.svg";
import {
EditableContainer,
EditableInnerContainer,
RichTextComponentContainer,
SlateButtonsContainer,
} from "./RichTextComponent.styled";
import { useState } from "react";
import BlockButton from "./BlockButton/BlockButton";
import ColorButton from "./ColorButton/ColorButton";
import selectedTheme from "../../themes";
import useIsMobile from "../../hooks/useIsMobile";
import { useTranslation } from "react-i18next";
import { isJsonString } from "../../util/helpers/jsonHelper";
import { useEffect } from "react";

const RichTextComponent = (props) => {
const { isMobile } = useIsMobile();
const { t } = useTranslation();
const [selectedColor, setSelectedColor] = useState({
color: selectedTheme.colors.colorPicker.darkGray,
colorName: "darkGray",
});
const [value, setValue] = useState([
{
type: "paragraph",
children: [{ text: "" }],
},
]);

useEffect(() => {
setValue(props?.value);
editor.children = isJsonString(props?.value)
? JSON.parse(props?.value)
: value;
}, [props?.value]);

const renderElement = useCallback(
(props) => <RichTextElement {...props} />,
[]
);
const renderLeaf = useCallback((props) => <RichTextLeaf {...props} />, []);
const editor = useMemo(() => withReact(createEditor()), [props?.value]);

return (
<RichTextComponentContainer
offerCard={props?.offerCard}
readOnly={props?.readOnly}
itemDetails={props?.itemDetails}
>
<Slate
editor={editor}
value={isJsonString(props?.value) ? JSON.parse(props?.value) : value}
onChange={(newValue) => {
if (props?.onChange) props?.onChange(JSON.stringify(newValue));
else setValue(newValue);
}}
>
{!isMobile && (
<SlateButtonsContainer readOnly={props?.readOnly}>
<MarkButton format="bold" icon="B" />
<MarkButton format="italic" icon="I" />
<MarkButton format="underline" icon="U" />
<BlockButton format="bulleted-list" icon={<BulletedList />} />
<ColorButton
selectedColor={selectedColor}
setSelectedColor={setSelectedColor}
/>
</SlateButtonsContainer>
)}
<EditableContainer
offerCard={props?.offerCard}
itemDetails={props?.itemDetails}
>
<EditableInnerContainer readOnly={props?.readOnly}>
<Editable
placeholder={t("offer.description")}
renderElement={renderElement}
renderLeaf={renderLeaf}
spellCheck
autoFocus
readOnly={props?.readOnly}
/>
</EditableInnerContainer>
</EditableContainer>
</Slate>
</RichTextComponentContainer>
);
};

// const initialValue = [
// {
// type: "paragraph",
// children: [{ text: "" }],
// },
// ];
RichTextComponent.propTypes = {
children: PropTypes.node,
onChange: PropTypes.func,
value: PropTypes.any,
readOnly: PropTypes.bool,
offerCard: PropTypes.bool,
itemDetails: PropTypes.bool,
};

export default RichTextComponent;

+ 76
- 0
src/components/RichTextComponent/RichTextComponent.styled.js Ver fichero

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

export const SlateButtonsContainer = styled(Box)`
display: ${(props) => (props.readOnly ? "none" : "flex")};
flex-direction: row;
height: 36px;
background-color: ${selectedTheme.colors.stylingTextBackground};
padding: 8px 16px;
border-radius: 4px;
gap: 20px;
`;
export const RichTextComponentContainer = styled(Box)`
width: 100%;
position: relative;
top: 14px;
height: ${(props) =>
props?.offerCard || props?.itemDetails ? "auto" : "140px"};
${(props) =>
props?.offerCard &&
css`
max-height: 100px;
display: -webkit-box;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
`}
border-radius: 4px;
border: ${(props) =>
props.readOnly ? "0" : `1px solid ${selectedTheme.colors.borderNormal}`};
margin-bottom: 14px;
@media (max-width: 600px) {
/* height: 40px; */
height: auto;
}
`;
export const EditableContainer = styled(Box)`
${(props) => !props?.itemDetails && "max-height: 104px;"}
${(props) => !props?.offerCard && "overflow: auto;"}
&::-webkit-scrollbar {
width: 5px;
height: 5px;
}
&::-webkit-scrollbar-track {
background: #ddd;
}
&::-webkit-scrollbar-thumb {
background: #777;
}
scrollbar-width: thin;
scrollbar-color: #ddd;
@media (max-width: 600px) {
/* max-height: 39px; */
}
`;
export const EditableInnerContainer = styled(Box)`
padding: ${(props) => (props?.readOnly ? "0" : "10px 16px")};
& > div {
min-height: 84px;
}
& * {
font-family: ${selectedTheme.fonts.textFont};
}
& span[data-slate-placeholder="true"] {
font-style: italic;
color: ${selectedTheme.colors.colorPicker.darkGray};
}
@media (max-width: 600px) {
& > div {
min-height: 0;
}
& * {
font-size: 12px;
}
}
`;

+ 70
- 0
src/components/RichTextComponent/RichTextElement/RichTextElement.js Ver fichero

@@ -0,0 +1,70 @@
import React from "react";
import PropTypes from "prop-types";
import { ListContainer } from "./RichTextElement.styled";
import selectedTheme from "../../../themes";

const RichTextElement = ({ attributes, children, element }) => {
const style = { textAlign: element.align };
switch (element.type) {
case "block-quote":
return (
<blockquote style={style} {...attributes}>
{children}
</blockquote>
);
case "bulleted-list":
return (
<ListContainer style={style} {...attributes}>
{children}
</ListContainer>
);
case "link":
return (
<a href="" style={style} {...attributes}>
{children}
</a>
);
case "heading-one":
return (
<h1 style={style} {...attributes}>
{children}
</h1>
);
case "heading-two":
return (
<h2 style={style} {...attributes}>
{children}
</h2>
);
case "list-item":
return (
<li style={style} {...attributes}>
{children}
</li>
);
case "numbered-list":
return (
<ol style={style} {...attributes}>
{children}
</ol>
);
default:
return (
<div
style={style}
{...attributes}
color={selectedTheme.colors.colorPicker.darkGray}
>
{children}
</div>
);
}
};

RichTextElement.propTypes = {
attributes: PropTypes.any,
children: PropTypes.any,
element: PropTypes.any,
};

export default RichTextElement;

+ 10
- 0
src/components/RichTextComponent/RichTextElement/RichTextElement.styled.js Ver fichero

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

export const ListContainer = styled(Box)`
color: green;
padding-left: 18px;
& > div {
display: list-item;
}
`;

+ 31
- 0
src/components/RichTextComponent/RichTextLeaf/RichTextLeaf.js Ver fichero

@@ -0,0 +1,31 @@
import React from "react";
import PropTypes from "prop-types";
const RichTextLeaf = ({ attributes, children, leaf }) => {
if (leaf.bold) {
children = <strong>{children}</strong>;
}

if (leaf.code) {
children = <code>{children}</code>;
}

if (leaf.italic) {
children = <i>{children}</i>;
}

if (leaf.underline) {
children = <u>{children}</u>;
}
if (leaf.color) {
children = <span style={{ color: leaf.color }}>{children}</span>;
}
return <span {...attributes}>{children}</span>;
};

RichTextLeaf.propTypes = {
attributes: PropTypes.any,
children: PropTypes.any,
leaf: PropTypes.any,
selectedColor: PropTypes.any,
};
export default RichTextLeaf;

+ 4
- 1
src/components/TextFields/TextField/TextField.js Ver fichero

@@ -14,6 +14,7 @@ export const TextField = React.forwardRef((props, ref) => {
setIsFieldEmpty(false);
}
}, [props.value]);
console.log(props);
return (
<TextFieldContainer
style={props.containerStyle}
@@ -21,6 +22,7 @@ export const TextField = React.forwardRef((props, ref) => {
height={props.height}
>
<TextFieldStyled
{...props.attributes}
inputRef={ref}
inputProps={props.inputProps}
placeholder={props.placeholder}
@@ -98,11 +100,12 @@ TextField.propTypes = {
endAdornment: PropTypes.node,
style: PropTypes.any,
}),
attributes: PropTypes.any,
};

TextField.defaultProps = {
italicPlaceholder: false,
showAnimation: false,
height: "48px",
font: selectedTheme.fonts.textFont
font: selectedTheme.fonts.textFont,
};

+ 1
- 0
src/constants/pages.js Ver fichero

@@ -26,3 +26,4 @@ export const ADMIN_CATEGORIES_PAGE = "/admin/categories";
export const ADMIN_LOCATIONS_PAGE = "/admin/locations";
export const ADMIN_PAYMENT_PAGE = "/admin/payment";
export const ADMIN_SUBCATEGORIES_PAGE = "/admin/categories/:categoryId";
export const MARKETPLACE_PAGE = "/marketplace";

+ 12
- 0
src/hooks/useIsLoggedIn.js Ver fichero

@@ -0,0 +1,12 @@
import { useSelector } from "react-redux";
import { selectUserId } from "../store/selectors/loginSelectors";

const useIsLoggedIn = () => {
const userId = useSelector(selectUserId);
const isLoggedIn = userId ? true : false;
return {
isLoggedIn
}
};

export default useIsLoggedIn;

+ 8
- 0
src/i18n/resources/rs.js Ver fichero

@@ -603,4 +603,12 @@ export default {
prevPage: "Previous page",
nextPage: "Next page",
},
colorPicker: {
label: "Boja: ",
darkGray: "Tamno Siva",
gray: "Siva",
yellow: "Žuta",
purple: "Ljubičasta",
pink: "Roze"
}
};

+ 3
- 86
src/pages/About/AboutPage.js Ver fichero

@@ -1,92 +1,9 @@
import React, { useEffect, useRef } from "react";
import React from "react";
import PropTypes from "prop-types";
import AboutComponent from "../../components/About/AboutComponent";
import { AboutPageContainer } from "./AboutPage.styled";
// import PricesComponent from "../../components/Prices/PricesComponent";
import PrivacyPolicyComponent from "../../components/PrivacyPolicy/PrivacyPolicyComponent";
import PricesComponent from "../../components/Prices/PricesComponent";
import { useLocation } from "react-router-dom";
import scrollConstants from "../../constants/scrollConstants";
import { useDispatch, useSelector } from "react-redux";
import { selectAboutRouteSelected } from "../../store/selectors/appSelectors";
import { setAboutRouteSelected } from "../../store/actions/app/appActions";
import AboutFooter from "../../components/Footer/AboutFooter";
import AboutPageContent from "../../components/About/AboutPageContent";

const AboutPage = () => {
const aboutRef = useRef(null);
const pricesRef = useRef(null);
const privacyPolicyRef = useRef(null);
const dispatch = useDispatch();
const aboutRouteSelected = useSelector(selectAboutRouteSelected);
const location = useLocation();
useEffect(() => {
if (location.state && location.state?.clicked) {
if (location.state.navigation === scrollConstants.about.aboutPage) {
window.scrollTo({ top: 0, behavior: "smooth" });
if (aboutRouteSelected !== scrollConstants.about.aboutPage) {
dispatch(setAboutRouteSelected(scrollConstants.about.aboutPage));
}
}
if (location.state.navigation === scrollConstants.about.pricesPage) {
const yAxis = pricesRef.current.offsetTop;
window.scrollTo({ top: yAxis, behavior: "smooth" });
if (aboutRouteSelected !== scrollConstants.about.pricesPage) {
dispatch(setAboutRouteSelected(scrollConstants.about.pricesPage));
}
}
if (
location.state.navigation === scrollConstants.about.privacyPolicyPage
) {
const yAxis = privacyPolicyRef.current.offsetTop - 64;
window.scrollTo({ top: yAxis, behavior: "smooth" });
if (aboutRouteSelected !== scrollConstants.about.privacyPolicyPage) {
dispatch(
setAboutRouteSelected(scrollConstants.about.privacyPolicyPage)
);
}
}
location.state = {};
}
}, [location]);

useEffect(() => {
const listener = () => {
if (
window.scrollY >
pricesRef.current.offsetTop - window.innerHeight / 2
) {
if (
window.scrollY >
privacyPolicyRef.current.offsetTop - window.innerHeight / 2
) {
if (aboutRouteSelected !== scrollConstants.about.privacyPolicyPage) {
dispatch(
setAboutRouteSelected(scrollConstants.about.privacyPolicyPage)
);
}
} else if (aboutRouteSelected !== scrollConstants.about.pricesPage) {
dispatch(setAboutRouteSelected(scrollConstants.about.pricesPage));
}
} else {
if (aboutRouteSelected !== scrollConstants.about.aboutPage) {
dispatch(setAboutRouteSelected(scrollConstants.about.aboutPage));
}
}
};
window.addEventListener("scroll", listener);
return () => window.removeEventListener("scroll", listener);
}, [aboutRouteSelected]);
return (
<AboutPageContainer>
<AboutComponent ref={aboutRef} id={scrollConstants.about.aboutPage} />
<PricesComponent ref={pricesRef} id={scrollConstants.about.pricesPage} />
<PrivacyPolicyComponent
ref={privacyPolicyRef}
id={scrollConstants.about.privacyPolicyPage}
/>
<AboutFooter />
</AboutPageContainer>
);
return <AboutPageContent />
};

AboutPage.propTypes = {

+ 0
- 12
src/pages/About/AboutPage.styled.js Ver fichero

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

export const AboutPageContainer = styled(Box)`
margin-top: 64px;
background-color: ${selectedTheme.colors.staticBackgroundColor};
position: relative;
@media (max-width: 600px) {
margin-top: 53px;
}
`

+ 33
- 22
src/pages/HomePage/HomePage.js Ver fichero

@@ -1,4 +1,5 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { HomePageContainer } from "./HomePage.styled";
import FilterCard from "../../components/Cards/FilterCard/FilterCard";
import MainLayout from "../../layouts/MainLayout/MainLayout";
@@ -7,8 +8,11 @@ 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 useIsLoggedIn from "../../hooks/useIsLoggedIn";
import AboutPageContent from "../../components/About/AboutPageContent";

const HomePage = () => {
const HomePage = (props) => {
const { isLoggedIn } = useIsLoggedIn();
const isLoadingOffers = useSelector(
selectIsLoadingByActionType(OFFERS_SCOPE)
);
@@ -17,26 +21,33 @@ const HomePage = () => {
const toggleFilters = () => {
setFiltersOpened((prevFiltersOpened) => !prevFiltersOpened);
};
return (
<HomePageContainer>
<MainLayout
leftCard={
<FilterCard
offers={offers}
filtersOpened={filtersOpened}
skeleton={isLoadingOffers}
toggleFilters={toggleFilters}
/>
}
content={
<MarketPlace
offers={offers}
skeleton={isLoadingOffers}
toggleFilters={toggleFilters}
/>
}
/>
</HomePageContainer>
);
if (isLoggedIn || props?.isMarketplacePage) {
return (
<HomePageContainer>
<MainLayout
leftCard={
<FilterCard
offers={offers}
filtersOpened={filtersOpened}
skeleton={isLoadingOffers}
toggleFilters={toggleFilters}
/>
}
content={
<MarketPlace
offers={offers}
skeleton={isLoadingOffers}
toggleFilters={toggleFilters}
/>
}
/>
</HomePageContainer>
);
}
return <AboutPageContent />;
};

HomePage.propTypes = {
isMarketplacePage: PropTypes.bool,
};
export default HomePage;

+ 8
- 0
src/pages/Marketplace/MarketplacePage.js Ver fichero

@@ -0,0 +1,8 @@
import React from "react";
import HomePage from "../HomePage/HomePage";

const MarketplacePage = () => {
return <HomePage isMarketplacePage />;
};

export default MarketplacePage;

+ 3
- 1
src/store/saga/offersSaga.js Ver fichero

@@ -48,6 +48,7 @@ import {
pinOfferSuccess,
pinOfferError,
addProfileOffers,
clearSelectedOffer,
// fetchAllOffersSuccess,
// fetchAllOffersError,
// setFeaturedOfferPage,
@@ -215,6 +216,7 @@ function* fetchOneOffer(payload) {
queryObject,
});
if (!data?.data) throw new Error();
yield put(clearSelectedOffer());
yield put(fetchOneOfferSuccess(data?.data));
} catch (e) {
console.log(e?.response?.status);
@@ -274,7 +276,7 @@ function* fetchProfileOffers(payload) {
} else {
queryString = yield select(selectQueryString);
}
console.log(queryString.toString());
let data;
if (payload.payload.isAdmin) {

+ 9
- 0
src/themes/primaryTheme/primaryThemeColors.js Ver fichero

@@ -29,6 +29,7 @@ export const primaryThemeColors = {
skeletonItemColor: "#F4F4F4",
chatHeaderColor: "#F4F4F4",
messageBackground: "#F4F4F4",
stylingTextBackground: "#F4F4F4",
messageText: "#1D1D1D",
messageDate: "#949494",
messageMyDate: "#C4C4C4",
@@ -41,4 +42,12 @@ export const primaryThemeColors = {
blockedColor: "#E4E4E4",
blockedTextColor: "#D13333",
errorColor: "#d32f2f",
colorPicker: {
darkGray: "#4D4D4D",
gray: "#B4B4B4",
yellow: "#FEB005",
purple: "#5A3984",
pink: "#E5D0FF",
border: "#818181",
},
};

+ 8
- 0
src/util/helpers/jsonHelper.js Ver fichero

@@ -0,0 +1,8 @@
export const isJsonString = (stringToCheck) => {
try {
JSON.parse(stringToCheck);
} catch (e) {
return false;
}
return true;
};

+ 68
- 3
yarn.lock Ver fichero

@@ -2178,6 +2178,11 @@
"resolved" "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz"
"version" "5.1.1"

"@types/is-hotkey@^0.1.1":
"integrity" "sha512-yB5C7zcOM7idwYZZ1wKQ3pTfjA9BbvFqRWvKB46GFddxnJtHwi/b9y84ykQtxQPg5qhdpg4Q/kWU3EGoCTmLzQ=="
"resolved" "https://registry.npmjs.org/@types/is-hotkey/-/is-hotkey-0.1.7.tgz"
"version" "0.1.7"

"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
"integrity" "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw=="
"resolved" "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz"
@@ -2215,7 +2220,7 @@
"resolved" "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
"version" "0.0.29"

"@types/lodash@^4.14.175":
"@types/lodash@^4.14.149", "@types/lodash@^4.14.175":
"integrity" "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q=="
"resolved" "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz"
"version" "4.14.182"
@@ -4023,6 +4028,11 @@
"safe-buffer" "5.1.2"
"vary" "~1.1.2"

"compute-scroll-into-view@^1.0.17":
"integrity" "sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg=="
"resolved" "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz"
"version" "1.0.17"

"concat-map@0.0.1":
"integrity" "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
"resolved" "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
@@ -4811,6 +4821,11 @@
dependencies:
"path-type" "^4.0.0"

"direction@^1.0.3":
"integrity" "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ=="
"resolved" "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz"
"version" "1.0.4"

"dns-equal@^1.0.0":
"integrity" "sha1-s55/HabrCnW6nBcySzR1PEfgZU0="
"resolved" "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz"
@@ -6599,6 +6614,11 @@
"resolved" "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz"
"version" "5.1.8"

"immer@^9.0.6":
"integrity" "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ=="
"resolved" "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz"
"version" "9.0.16"

"immer@^9.0.7":
"integrity" "sha512-ubBeqQutOSLIFCUBN03jGeOS6a3DoYlSYwYJTa+gSKEZKU5redJIqkIdZ3JVv/4RZpfcXdAWH5zCNLWPRv2WDw=="
"resolved" "https://registry.npmjs.org/immer/-/immer-9.0.14.tgz"
@@ -6958,6 +6978,11 @@
dependencies:
"is-extglob" "^2.1.1"

"is-hotkey@^0.1.6":
"integrity" "sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ=="
"resolved" "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.1.8.tgz"
"version" "0.1.8"

"is-installed-globally@^0.4.0":
"integrity" "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ=="
"resolved" "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz"
@@ -7044,6 +7069,11 @@
dependencies:
"isobject" "^3.0.1"

"is-plain-object@^5.0.0":
"integrity" "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
"resolved" "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz"
"version" "5.0.0"

"is-potential-custom-element-name@^1.0.1":
"integrity" "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
"resolved" "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz"
@@ -8143,7 +8173,7 @@
"resolved" "https://registry.npmjs.org/lodash.unset/-/lodash.unset-4.5.2.tgz"
"version" "4.5.2"

"lodash@^4.17.11", "lodash@^4.17.14", "lodash@^4.17.15", "lodash@^4.17.19", "lodash@^4.17.20", "lodash@^4.17.21", "lodash@^4.17.5", "lodash@^4.7.0", "lodash@>=3.5 <5", "lodash@4":
"lodash@^4.17.11", "lodash@^4.17.14", "lodash@^4.17.15", "lodash@^4.17.19", "lodash@^4.17.20", "lodash@^4.17.21", "lodash@^4.17.4", "lodash@^4.17.5", "lodash@^4.7.0", "lodash@>=3.5 <5", "lodash@4":
"integrity" "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
"resolved" "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
"version" "4.17.21"
@@ -10369,7 +10399,7 @@
"strip-ansi" "6.0.0"
"text-table" "0.2.0"

"react-dom@*", "react-dom@^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.6.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.0 || ^17.0.0", "react-dom@^17.0.0 || ^18.0.0", "react-dom@^17.0.2", "react-dom@>= 16.8.0", "react-dom@>=16", "react-dom@>=16.6.0":
"react-dom@*", "react-dom@^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.6.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.0 || ^17.0.0", "react-dom@^17.0.0 || ^18.0.0", "react-dom@^17.0.2", "react-dom@>= 16.8.0", "react-dom@>=16", "react-dom@>=16.6.0", "react-dom@>=16.8.0":
"integrity" "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA=="
"resolved" "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
"version" "17.0.2"
@@ -11337,6 +11367,13 @@
"ajv" "^6.12.5"
"ajv-keywords" "^3.5.2"

"scroll-into-view-if-needed@^2.2.20":
"integrity" "sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg=="
"resolved" "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz"
"version" "2.2.29"
dependencies:
"compute-scroll-into-view" "^1.0.17"

"section-iterator@^2.0.0":
"integrity" "sha512-xvTNwcbeDayXotnV32zLb3duQsP+4XosHpb/F+tu6VzEZFmIjzPdNk6/O+QOOx5XTh08KL2ufdXeCO33p380pQ=="
"resolved" "https://registry.npmjs.org/section-iterator/-/section-iterator-2.0.0.tgz"
@@ -11609,6 +11646,29 @@
"resolved" "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz"
"version" "3.0.0"

"slate-react@^0.86.0":
"integrity" "sha512-FlzG0CL1MtpZk7DRH6a101FdqdhTi+vZ23SbY5hQudIdgLo7QnUBv97BRe30VctqsL9L+Z2OsFHSCmWpIIxJAw=="
"resolved" "https://registry.npmjs.org/slate-react/-/slate-react-0.86.0.tgz"
"version" "0.86.0"
dependencies:
"@types/is-hotkey" "^0.1.1"
"@types/lodash" "^4.14.149"
"direction" "^1.0.3"
"is-hotkey" "^0.1.6"
"is-plain-object" "^5.0.0"
"lodash" "^4.17.4"
"scroll-into-view-if-needed" "^2.2.20"
"tiny-invariant" "1.0.6"

"slate@^0.86.0", "slate@>=0.65.3":
"integrity" "sha512-aexL720Tpqx6St5oz0jo0/wZWCT7z8lChKkVJo2eiFkSNmoSnCGR62d2itAvmse7dEXkl4DbfAxRQPj8JDuGTg=="
"resolved" "https://registry.npmjs.org/slate/-/slate-0.86.0.tgz"
"version" "0.86.0"
dependencies:
"immer" "^9.0.6"
"is-plain-object" "^5.0.0"
"tiny-warning" "^1.0.3"

"slice-ansi@^4.0.0":
"integrity" "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ=="
"resolved" "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz"
@@ -12398,6 +12458,11 @@
"resolved" "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz"
"version" "1.2.0"

"tiny-invariant@1.0.6":
"integrity" "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA=="
"resolved" "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz"
"version" "1.0.6"

"tiny-warning@^1.0.0", "tiny-warning@^1.0.2", "tiny-warning@^1.0.3":
"integrity" "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
"resolved" "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz"

Cargando…
Cancelar
Guardar