| @@ -36,6 +36,7 @@ export const AboutSectionImage = styled.img` | |||
| flex: 1; | |||
| width: 648px; | |||
| height: 345px; | |||
| object-fit: cover; | |||
| `; | |||
| export const AboutSectionTextContainer = styled(Box)` | |||
| display: flex; | |||
| @@ -47,7 +47,7 @@ export const ArrowContainer = styled(IconButton)` | |||
| ${(props) => | |||
| props.disabled && | |||
| ` | |||
| border 1px solid ${selectedTheme.colors.iconStrokeDisabledColor}; | |||
| border 1px solid ${selectedTheme.colors.iconStrokeDisabledColor} !important; | |||
| &:hover { | |||
| background-color: inherit; | |||
| & svg path { | |||
| @@ -224,3 +224,9 @@ export const NextButtonContainer = styled(PrimaryButton)` | |||
| } | |||
| } | |||
| `; | |||
| export const ErrorText = styled(Typography)` | |||
| color: red; | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| height: 20px; | |||
| font-size: 14px; | |||
| `; | |||
| @@ -0,0 +1,28 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { ErrorText } from "../../CreateOffer.styled"; | |||
| const ErrorMessage = (props) => { | |||
| const formik = props.formik; | |||
| return ( | |||
| <> | |||
| {formik.errors.nameOfProduct ? ( | |||
| <ErrorText>{formik.errors.nameOfProduct}</ErrorText> | |||
| ) : formik.errors.description ? ( | |||
| <ErrorText>{formik.errors.description}</ErrorText> | |||
| ) : formik.errors.location ? ( | |||
| <ErrorText>{formik.errors.location}</ErrorText> | |||
| ) : formik.errors.category ? ( | |||
| <ErrorText>{formik.errors.category}</ErrorText> | |||
| ) : formik.errors.subcategory ? ( | |||
| <ErrorText>{formik.errors.subcategory}</ErrorText> | |||
| ) : (<></>)} | |||
| </> | |||
| ); | |||
| }; | |||
| ErrorMessage.propTypes = { | |||
| formik: PropTypes.any, | |||
| }; | |||
| export default ErrorMessage; | |||
| @@ -12,6 +12,7 @@ import OfferSubcategoryField from "./OfferSubcategoryField/OfferSubcategoryField | |||
| import NextButton from "./NextButton/NextButton"; | |||
| import firstPartCreateOfferInitialValues from "../../../../initialValues/createOfferInitialValues/firstPartCreateOfferInitialValues"; | |||
| import firstPartCreateOfferValidation from "../../../../validations/createOfferValidation/firstPartCreateOfferValidation"; | |||
| import ErrorMessage from "./ErrorMessage/ErrorMessage"; | |||
| const FirstPartCreateOffer = (props) => { | |||
| const [subcategories, setSubcategories] = useState([]); | |||
| @@ -67,6 +68,7 @@ const FirstPartCreateOffer = (props) => { | |||
| handleSubcategories={handleSubcategories} | |||
| /> | |||
| <OfferSubcategoryField formik={formik} subcategories={subcategories} /> | |||
| <ErrorMessage formik={formik} /> | |||
| <NextButton formik={formik} /> | |||
| </CreateOfferFormContainer> | |||
| ); | |||
| @@ -15,6 +15,8 @@ export const InfoIcon = styled(Box)` | |||
| export const InfoText = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| text-transform: capitalize; | |||
| color: ${selectedTheme.colors.primaryText}; | |||
| font-size: 12px; | |||
| @media (max-width: 600px) { | |||
| font-size: 12px; | |||
| } | |||
| @@ -185,7 +185,7 @@ export const ProfileCardHeader = styled(Grid)` | |||
| export const HeaderTitle = styled(Typography)` | |||
| font-size: 16px; | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| color: ${selectedTheme.colors.primaryDarkTextThird}; | |||
| color: ${selectedTheme.colors.primaryText}; | |||
| position: relative; | |||
| @media (max-width: 600px) { | |||
| font-size: 12px; | |||
| @@ -2,4 +2,5 @@ import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| export const DirectChatContainer = styled(Box)` | |||
| margin-bottom: 8px; | |||
| ` | |||
| @@ -1,21 +1,18 @@ | |||
| import { Box, Link, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import {ReactComponent as Message} from "../../../assets/images/svg/message.svg"; | |||
| import { ReactComponent as Message } from "../../../assets/images/svg/message.svg"; | |||
| import selectedTheme from "../../../themes"; | |||
| export const DirectChatHeaderTitleContainer = styled(Box)` | |||
| ` | |||
| export const MessageIcon = styled(Message)` | |||
| ` | |||
| export const DirectChatHeaderTitleContainer = styled(Box)``; | |||
| export const MessageIcon = styled(Message)``; | |||
| export const HeaderTitleContent = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-size: 16px; | |||
| position: relative; | |||
| left: 9px; | |||
| bottom: 7px; | |||
| ` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-size: 16px; | |||
| position: relative; | |||
| left: 9px; | |||
| bottom: 7px; | |||
| color: ${selectedTheme.colors.primaryText}; | |||
| `; | |||
| export const ButtonContainer = styled(Link)` | |||
| width: fit-content; | |||
| cursor: pointer; | |||
| @@ -21,5 +21,6 @@ export const HeaderTitleContent = styled(Typography)` | |||
| font-size: 16px; | |||
| position: relative; | |||
| left: 9px; | |||
| color: ${selectedTheme.colors.primaryText}; | |||
| bottom: 7px; | |||
| ` | |||
| @@ -19,4 +19,7 @@ export const LinkRoute = styled(Link)` | |||
| font-size: 16px; | |||
| line-height: 22px; | |||
| letter-spacing: 0.02em; | |||
| &:hover { | |||
| border-bottom: 1px solid ${selectedTheme.colors.iconYellowColor}; | |||
| } | |||
| `; | |||
| @@ -45,12 +45,14 @@ const Header = (props) => { | |||
| const { isMobile } = useIsMobile(); | |||
| // Changing header string on refresh or on load | |||
| const showAltString = useMemo(() => { | |||
| return ( | |||
| sorting.selectedSortOption === sortEnum.INITIAL || | |||
| sorting.selectedSortOption === sortEnum.NEW || | |||
| !sorting.selectedSortOption | |||
| ); | |||
| const altString = useMemo(() => { | |||
| if (sorting.selectedSortOption === sortEnum.OLD) { | |||
| return t("header.oldOffers"); | |||
| } | |||
| if (sorting.selectedSortOption === sortEnum.POPULAR) { | |||
| return t("header.popularOffers"); | |||
| } | |||
| return t("header.newOffers"); | |||
| }, [sorting.selectedSortOption]); | |||
| const handleChangeSelect = (event) => { | |||
| @@ -62,7 +64,7 @@ const Header = (props) => { | |||
| return ( | |||
| <> | |||
| <SkeletonHeader skeleton={props.skeleton} myOffers={props.myOffers}/> | |||
| <SkeletonHeader skeleton={props.skeleton} myOffers={props.myOffers} /> | |||
| <HeaderWrapperContainer skeleton={props.skeleton}> | |||
| <HeaderContainer> | |||
| {/* Setting appropriate header title if page is market place or my offers */} | |||
| @@ -71,14 +73,10 @@ const Header = (props) => { | |||
| {!props.myOffers ? ( | |||
| <> | |||
| <CategoryHeaderIcon /> | |||
| <HeaderLocation initial={showAltString}> | |||
| <HeaderLocation initial={altString}> | |||
| {headerString} | |||
| </HeaderLocation> | |||
| {showAltString && ( | |||
| <HeaderAltLocation> | |||
| {t("header.newOffers")} | |||
| </HeaderAltLocation> | |||
| )} | |||
| <HeaderAltLocation>{altString}</HeaderAltLocation> | |||
| </> | |||
| ) : ( | |||
| <ButtonContainer onClick={goBack}> | |||
| @@ -1,6 +1,6 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { Arrow, ButtonContainer, LinkText, PricesHeaderContainer, PricesHeaderTitle } from "./PricesHeader.styled"; | |||
| import { Arrow, ButtonContainer, LinkText, PricesHeaderContainer, PricesHeaderLine, PricesHeaderTitle } from "./PricesHeader.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const PricesHeader = () => { | |||
| @@ -8,6 +8,7 @@ const PricesHeader = () => { | |||
| return ( | |||
| <PricesHeaderContainer> | |||
| <PricesHeaderTitle>{t("prices.header.title")}</PricesHeaderTitle> | |||
| <PricesHeaderLine /> | |||
| <ButtonContainer> | |||
| <LinkText>{t("prices.header.link")}</LinkText> | |||
| <Arrow /> | |||
| @@ -13,9 +13,14 @@ export const PricesHeaderTitle = styled(Typography)` | |||
| display: flex; | |||
| flex: 1; | |||
| `; | |||
| export const PricesHeaderLine = styled(Box)` | |||
| border-top: 1px solid ${selectedTheme.colors.iconYellowColor}; | |||
| width: 90px; | |||
| margin-bottom: 26px; | |||
| `; | |||
| export const PricesHeaderContainer = styled(Box)` | |||
| display: flex; | |||
| flex-direction: row; | |||
| flex-direction: column; | |||
| width: 100%; | |||
| justify-content: space-between; | |||
| `; | |||
| @@ -12,7 +12,7 @@ import { sortReviews } from "../../../util/helpers/reviewsHelper"; | |||
| import { reviewSortEnum } from "../../../enums/reviewEnum"; | |||
| import { setReviews } from "../../../store/actions/review/reviewActions"; | |||
| const ReviewsSorting = () => { | |||
| const ReviewsSorting = (props) => { | |||
| const reviews = useSelector(selectSelectedReviews); | |||
| const dispatch = useDispatch(); | |||
| const [value, setValue] = useState(); | |||
| @@ -25,6 +25,7 @@ const ReviewsSorting = () => { | |||
| ) | |||
| ) | |||
| ); | |||
| props.changeSorting(); | |||
| setValue(event.target.value); | |||
| }; | |||
| return ( | |||
| @@ -53,6 +54,7 @@ const ReviewsSorting = () => { | |||
| ReviewsSorting.propTypes = { | |||
| children: PropTypes.node, | |||
| changeSorting: PropTypes.func, | |||
| }; | |||
| export default ReviewsSorting; | |||
| @@ -1,4 +1,4 @@ | |||
| import React, { useEffect, useMemo } from "react"; | |||
| import React, { useEffect, useMemo, useRef } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| ReviewList, | |||
| @@ -33,6 +33,7 @@ const UserReviews = (props) => { | |||
| const reviews = useSelector(selectSelectedReviews); | |||
| const routeMatch = useRouteMatch(); | |||
| const dispatch = useDispatch(); | |||
| const listRef = useRef(null); | |||
| const isLoadingReview = useSelector( | |||
| selectIsLoadingByActionType( | |||
| @@ -67,6 +68,10 @@ const UserReviews = (props) => { | |||
| return []; | |||
| }, [props.profileReviews, offer, props.isProfileReviews, reviews]); | |||
| const scrollToTop = () => { | |||
| listRef.current.scrollTo({ top: 0, behaviour: "smooth" }); | |||
| }; | |||
| return ( | |||
| <> | |||
| {!props.givingReview && | |||
| @@ -93,10 +98,10 @@ const UserReviews = (props) => { | |||
| /> | |||
| <ReviewsTitle>{t("reviews.rates")}</ReviewsTitle> | |||
| </ReviewsHeaderTitle> | |||
| <ReviewsSorting /> | |||
| <ReviewsSorting changeSorting={scrollToTop} /> | |||
| </ReviewsHeader> | |||
| )} | |||
| <ReviewList isProfileReviews={props.isProfileReviews}> | |||
| <ReviewList ref={listRef} isProfileReviews={props.isProfileReviews}> | |||
| {lastThreeReviews?.length > 0 ? ( | |||
| lastThreeReviews?.map((review, index) => ( | |||
| <UserReviewsCard | |||
| @@ -69,6 +69,12 @@ export default { | |||
| useDifferentEmail: "Iskoristite drugačiju lozinku.", | |||
| wrongCredentials: "Pogrešan mail ili lozinka!", | |||
| headerTitle: "Ulogujte se", | |||
| nameOfProductError: "Naslov je obavezan!", | |||
| descriptionRequiredError: "Opis je obavezan!", | |||
| descriptionNoOfCharsError: "Opis mora imati najmanje 8 karaktera!", | |||
| locationError: "Unesite ispravnu lokaciju!", | |||
| categoryError: "Kategorija je obavezna!", | |||
| subcategoryError: "Podkategorija je obavezna!" | |||
| }, | |||
| password: { | |||
| weak: "slaba", | |||
| @@ -189,6 +195,8 @@ export default { | |||
| checkEverything: "POGLEDAJ SVE", | |||
| myMessages: "Moje poruke", | |||
| newOffers: "Najnovije ponude", | |||
| oldOffers: "Najstarije ponude", | |||
| popularOffers: "Najpopularnije ponude", | |||
| navMenu: "Navigacioni Meni", | |||
| noItems: "Trenutno nema ni jedne stavke...", | |||
| logout: "Odjavite se", | |||
| @@ -3,13 +3,20 @@ import i18next from "i18next"; | |||
| export default (locations, categories, subcategories) => | |||
| Yup.object().shape({ | |||
| nameOfProduct: Yup.string().required( | |||
| i18next.t("login.nameOfProductRequired") | |||
| ), | |||
| nameOfProduct: Yup.string().required(i18next.t("offer.nameOfProductError")), | |||
| description: Yup.string() | |||
| .required(i18next.t("login.descriptionRequired")) | |||
| .min(8), | |||
| location: Yup.string().oneOf(locations.map((l) => l.city)), | |||
| category: Yup.string().oneOf(categories.map((c) => c.name)), | |||
| subcategory: Yup.string().oneOf(subcategories || []), | |||
| .required(i18next.t("offer.descriptionRequiredError")) | |||
| .min(8, i18next.t("offer.descriptionNoOfCharsError")), | |||
| location: Yup.string().oneOf( | |||
| locations.map((l) => l.city), | |||
| i18next.t("offer.locationError") | |||
| ), | |||
| category: Yup.string().oneOf( | |||
| categories.map((c) => c.name), | |||
| i18next.t("offer.categoryError") | |||
| ), | |||
| subcategory: Yup.string().oneOf( | |||
| subcategories || [], | |||
| i18next.t("offer.subcategoryError") | |||
| ), | |||
| }); | |||