Pārlūkot izejas kodu

Finished app

feature/1737
Djordje Mitrovic pirms 3 gadiem
vecāks
revīzija
02353fb5f9
36 mainītis faili ar 603 papildinājumiem un 252 dzēšanām
  1. 8
    1
      src/AppRoutes.js
  2. 95
    0
      src/components/About/AboutPageContent.js
  3. 12
    0
      src/components/About/AboutPageContent.styled.js
  4. 2
    2
      src/components/About/CheckOffersButton/CheckOffersButton.js
  5. 2
    2
      src/components/Cards/CreateOfferCard/CreateOffer.styled.js
  6. 7
    18
      src/components/Cards/CreateOfferCard/FirstPart/OfferDescriptionField/OfferDescriptionField.js
  7. 4
    1
      src/components/Cards/CreateOfferCard/FirstPart/OfferTitleField/OfferTitleField.styled.js
  8. 25
    0
      src/components/Cards/ItemDetailsCard/OfferDetails/OfferDescription/OfferDescription.js
  9. 13
    0
      src/components/Cards/ItemDetailsCard/OfferDetails/OfferDescription/OfferDescription.styled.js
  10. 2
    4
      src/components/Cards/ItemDetailsCard/OfferDetails/OfferDetails.js
  11. 0
    7
      src/components/Cards/ItemDetailsCard/OfferDetails/OfferDetails.styled.js
  12. 9
    1
      src/components/Cards/OfferCard/OfferDescription/OfferDescription.js
  13. 1
    1
      src/components/Footer/AboutFooter.styled.js
  14. 24
    8
      src/components/Header/Header.js
  15. 8
    3
      src/components/Header/SearchInput/SearchInput.js
  16. 54
    0
      src/components/Popovers/ColorPicker/ColorPicker.js
  17. 43
    0
      src/components/Popovers/ColorPicker/ColorPicker.styled.js
  18. 1
    1
      src/components/RichTextComponent/BlockButton/BlockButton.js
  19. 40
    25
      src/components/RichTextComponent/ColorButton/ColorButton.js
  20. 29
    0
      src/components/RichTextComponent/ColorButton/ColorButton.styled.js
  21. 26
    0
      src/components/RichTextComponent/ColorIcon/ColorIcon.js
  22. 15
    0
      src/components/RichTextComponent/ColorIcon/ColorIcon.styled.js
  23. 55
    43
      src/components/RichTextComponent/RichTextComponent.js
  24. 37
    7
      src/components/RichTextComponent/RichTextComponent.styled.js
  25. 12
    1
      src/components/RichTextComponent/RichTextElement/RichTextElement.js
  26. 6
    5
      src/components/RichTextComponent/RichTextElement/RichTextElement.styled.js
  27. 4
    1
      src/components/RichTextComponent/RichTextLeaf/RichTextLeaf.js
  28. 1
    0
      src/constants/pages.js
  29. 12
    0
      src/hooks/useIsLoggedIn.js
  30. 1
    0
      src/i18n/resources/rs.js
  31. 3
    86
      src/pages/About/AboutPage.js
  32. 0
    12
      src/pages/About/AboutPage.styled.js
  33. 33
    22
      src/pages/HomePage/HomePage.js
  34. 8
    0
      src/pages/Marketplace/MarketplacePage.js
  35. 3
    1
      src/store/saga/offersSaga.js
  36. 8
    0
      src/util/helpers/jsonHelper.js

+ 8
- 1
src/AppRoutes.js Parādīt failu

@@ -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} />

+ 95
- 0
src/components/About/AboutPageContent.js Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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; */

+ 7
- 18
src/components/Cards/CreateOfferCard/FirstPart/OfferDescriptionField/OfferDescriptionField.js Parādīt failu

@@ -1,33 +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 ? (
<RichTextComponent />
) : (
<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}
/>
</>
);
};

+ 4
- 1
src/components/Cards/CreateOfferCard/FirstPart/OfferTitleField/OfferTitleField.styled.js Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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;
`;

+ 1
- 1
src/components/RichTextComponent/BlockButton/BlockButton.js Parādīt failu

@@ -3,7 +3,7 @@ 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"];
const LIST_TYPES = ["numbered-list", "bulleted-list", "link"];
const TEXT_ALIGN_TYPES = ["left", "center", "right", "justify"];
const BlockButton = ({ format, icon }) => {
const editor = useSlate();

+ 40
- 25
src/components/RichTextComponent/ColorButton/ColorButton.js Parādīt failu

@@ -2,42 +2,57 @@ import React from "react";
import PropTypes from "prop-types";
import { useSlate } from "slate-react";
import { Editor } from "slate";
import { MarkButtonContainer, MarkButtonIcon } from "../MarkButton/MarkButton.styled";
import { ColorButtonContainer, ColorButtonIcon } from "./ColorButton.styled";
import { useState } from "react";
import ColorIcon from "../ColorIcon/ColorIcon";
import ColorPicker from "../../Popovers/ColorPicker/ColorPicker";

const toggleMark = (editor, format) => {
const isActive = isMarkActive(editor, format);
// const colorMarks = ["darkGray", "gray", "purple", "pink", "yellow"];

if (isActive) {
Editor.removeMark(editor, format);
} else {
Editor.addMark(editor, format, true);
}
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 isMarkActive = (editor, format) => {
// const marks = Editor.marks(editor);
// return marks ? marks[format] === true : false;
// };

const ColorButton = ({ format, icon }) => {
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 (
<MarkButtonContainer
active={isMarkActive(editor, format)}
format={format}
onMouseDown={(event) => {
event.preventDefault();
toggleMark(editor, format);
}}
>
<MarkButtonIcon format={format} active={isMarkActive(editor, format)}>
{icon}
</MarkButtonIcon>
</MarkButtonContainer>
<>
<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 Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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"};
`;

+ 55
- 43
src/components/RichTextComponent/RichTextComponent.js Parādīt failu

@@ -1,6 +1,7 @@
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";
@@ -16,74 +17,77 @@ 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 COLORS = [
{
const RichTextComponent = (props) => {
const { isMobile } = useIsMobile();
const { t } = useTranslation();
const [selectedColor, setSelectedColor] = useState({
color: selectedTheme.colors.colorPicker.darkGray,
i18key: "colorPicker.darkGray",
},
{
color: selectedTheme.colors.colorPicker.gray,
i18key: "colorPicker.gray",
},
{
color: selectedTheme.colors.colorPicker.yellow,
i18key: "colorPicker.yellow",
},
{
color: selectedTheme.colors.colorPicker.purple,
i18key: "colorPicker.purple",
},
{
color: selectedTheme.colors.colorPicker.pink,
i18key: "colorPicker.pink",
},
];

const RichTextComponent = () => {
const [color, setColor] = useState(COLORS[0]);
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()), []);
const editor = useMemo(() => withReact(createEditor()), [props?.value]);

return (
<RichTextComponentContainer>
<RichTextComponentContainer
offerCard={props?.offerCard}
readOnly={props?.readOnly}
itemDetails={props?.itemDetails}
>
<Slate
editor={editor}
value={value}
value={isJsonString(props?.value) ? JSON.parse(props?.value) : value}
onChange={(newValue) => {
console.log(newValue);
setValue(newValue);
if (props?.onChange) props?.onChange(JSON.stringify(newValue));
else setValue(newValue);
}}
>
<SlateButtonsContainer>
<MarkButton format="bold" icon="B" />
<MarkButton format="italic" icon="I" />
<MarkButton format="underline" icon="U" />
<BlockButton format="bulleted-list" icon={<BulletedList />} />
<ColorButton
format="color"
currentColor={color}
setCurrentColor={setColor}
colorValues={COLORS}
/>
</SlateButtonsContainer>
<EditableContainer>
<EditableInnerContainer>
{!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>
@@ -98,5 +102,13 @@ const RichTextComponent = () => {
// 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;

+ 37
- 7
src/components/RichTextComponent/RichTextComponent.styled.js Parādīt failu

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

export const SlateButtonsContainer = styled(Box)`
display: flex;
display: ${(props) => (props.readOnly ? "none" : "flex")};
flex-direction: row;
height: 36px;
background-color: ${selectedTheme.colors.stylingTextBackground};
@@ -15,13 +15,28 @@ export const RichTextComponentContainer = styled(Box)`
width: 100%;
position: relative;
top: 14px;
height: 140px;
border: 1px solid ${selectedTheme.colors.borderNormal};
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)`
max-height: 104px;
overflow: auto;
${(props) => !props?.itemDetails && "max-height: 104px;"}
${(props) => !props?.offerCard && "overflow: auto;"}
&::-webkit-scrollbar {
width: 5px;
height: 5px;
@@ -34,13 +49,28 @@ export const EditableContainer = styled(Box)`
}
scrollbar-width: thin;
scrollbar-color: #ddd;
@media (max-width: 600px) {
/* max-height: 39px; */
}
`;
export const EditableInnerContainer = styled(Box)`
padding: 10px 16px;
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;
}
}
`;

+ 12
- 1
src/components/RichTextComponent/RichTextElement/RichTextElement.js Parādīt failu

@@ -1,6 +1,7 @@
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 };
@@ -17,6 +18,12 @@ const RichTextElement = ({ attributes, children, element }) => {
{children}
</ListContainer>
);
case "link":
return (
<a href="" style={style} {...attributes}>
{children}
</a>
);
case "heading-one":
return (
<h1 style={style} {...attributes}>
@@ -43,7 +50,11 @@ const RichTextElement = ({ attributes, children, element }) => {
);
default:
return (
<div style={style} {...attributes}>
<div
style={style}
{...attributes}
color={selectedTheme.colors.colorPicker.darkGray}
>
{children}
</div>
);

+ 6
- 5
src/components/RichTextComponent/RichTextElement/RichTextElement.styled.js Parādīt failu

@@ -2,8 +2,9 @@ import { Box } from "@mui/material";
import styled from "styled-components";

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

+ 4
- 1
src/components/RichTextComponent/RichTextLeaf/RichTextLeaf.js Parādīt failu

@@ -16,7 +16,9 @@ const RichTextLeaf = ({ attributes, children, leaf }) => {
if (leaf.underline) {
children = <u>{children}</u>;
}

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

@@ -24,5 +26,6 @@ RichTextLeaf.propTypes = {
attributes: PropTypes.any,
children: PropTypes.any,
leaf: PropTypes.any,
selectedColor: PropTypes.any,
};
export default RichTextLeaf;

+ 1
- 0
src/constants/pages.js Parādīt failu

@@ -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 Parādīt failu

@@ -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;

+ 1
- 0
src/i18n/resources/rs.js Parādīt failu

@@ -603,6 +603,7 @@ export default {
nextPage: "Next page",
},
colorPicker: {
label: "Boja: ",
darkGray: "Tamno Siva",
gray: "Siva",
yellow: "Žuta",

+ 3
- 86
src/pages/About/AboutPage.js Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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 Parādīt failu

@@ -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) {

+ 8
- 0
src/util/helpers/jsonHelper.js Parādīt failu

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

Notiek ielāde…
Atcelt
Saglabāt