| @@ -0,0 +1,64 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import BackdropComponent from "../../../MUI/BackdropComponent"; | |||
| import { | |||
| CloseButton, | |||
| ImagesCarouselContainer, | |||
| ImagesCarouselHeader, | |||
| Offer, | |||
| OfferImage, | |||
| OfferSpan, | |||
| Scroller, | |||
| } from "./ImagesCarousel.styled"; | |||
| import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter"; | |||
| import useIsMobile from "../../../../hooks/useIsMobile"; | |||
| import { ReactComponent as CloseButtonIcon } from "../../../../assets/images/svg/close-modal.svg"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const ImagesCarousel = (props) => { | |||
| const { isMobile } = useIsMobile(); | |||
| const { t } = useTranslation(); | |||
| const closeCreateOfferModal = () => { | |||
| props.onModalClose(); | |||
| }; | |||
| return ( | |||
| <> | |||
| <BackdropComponent | |||
| isLoading | |||
| handleClose={closeCreateOfferModal} | |||
| position="fixed" | |||
| /> | |||
| <ImagesCarouselContainer> | |||
| <ImagesCarouselHeader> | |||
| {t("carousel.imagesReview")} | |||
| </ImagesCarouselHeader> | |||
| <CloseButton onClick={closeCreateOfferModal}> | |||
| <CloseButtonIcon /> | |||
| </CloseButton> | |||
| <Scroller> | |||
| {props?.offer?.offer?.images.map((image) => { | |||
| if (!image) return; | |||
| return ( | |||
| <OfferImage | |||
| src={getImageUrl(image, variants.carousel, isMobile)} | |||
| key={image} | |||
| /> | |||
| ); | |||
| })} | |||
| </Scroller> | |||
| <Offer> | |||
| {t("carousel.offer")} <OfferSpan>{props.offer.offer.name}</OfferSpan> | |||
| </Offer> | |||
| </ImagesCarouselContainer> | |||
| </> | |||
| ); | |||
| }; | |||
| ImagesCarousel.propTypes = { | |||
| offer: PropTypes.any, | |||
| onModalClose: PropTypes.any, | |||
| createOffer: PropTypes.bool, | |||
| }; | |||
| export default ImagesCarousel; | |||
| @@ -0,0 +1,100 @@ | |||
| import styled from "styled-components"; | |||
| import { Box } from "@mui/system"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import HorizontalScroller from "../../../Scroller/HorizontalScroller"; | |||
| export const ImagesCarouselContainer = styled(Box)` | |||
| width: 756px; | |||
| height: 774px; | |||
| position: fixed; | |||
| top: calc(50% - 387px); | |||
| left: calc(50% - 378px); | |||
| z-index: 150; | |||
| background-color: #fff; | |||
| padding: 0 18px 18px 18px; | |||
| display: flex; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| border-radius: 4px; | |||
| @media (max-width: 600px) { | |||
| width: 350px; | |||
| height: 383px; | |||
| overflow: hidden; | |||
| top: calc(50% - 191.5px); | |||
| left: calc(50% - 175px); | |||
| padding-bottom: 36px; | |||
| } | |||
| `; | |||
| export const ImagesCarouselHeader = styled(Box)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-size: 24px; | |||
| font-weight: 700; | |||
| color: ${selectedTheme.colors.primaryPurple}; | |||
| margin-top: 36px; | |||
| @media (max-width: 600px) { | |||
| font-size: 18px; | |||
| } | |||
| `; | |||
| export const Scroller = styled(HorizontalScroller)` | |||
| max-width: 100%; | |||
| max-height: 576px; | |||
| margin-top: 36px; | |||
| margin-bottom: 38px; | |||
| @media (max-width: 600px) { | |||
| margin-bottom: 18px; | |||
| } | |||
| `; | |||
| export const OfferImage = styled.img` | |||
| min-width: 576px; | |||
| min-height: 576px; | |||
| margin-right: 23px; | |||
| margin-left: 10px; | |||
| border-radius: 4px; | |||
| object-fit: cover; | |||
| @media (max-width: 600px) { | |||
| min-width: 216px; | |||
| min-height: 216px; | |||
| margin-right: 18px; | |||
| margin-left: 0; | |||
| } | |||
| `; | |||
| export const Offer = styled.p` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-size: 12px; | |||
| @media (max-width: 600px) { | |||
| font-size: 9px; | |||
| } | |||
| `; | |||
| export const OfferSpan = styled.span` | |||
| font-size: 16px; | |||
| font-weight: 700; | |||
| @media (max-width: 600px) { | |||
| font-size: 12px; | |||
| } | |||
| `; | |||
| export const CloseButton = styled(Box)` | |||
| position: absolute; | |||
| top: 42px; | |||
| right: 42px; | |||
| cursor: pointer; | |||
| @media (max-width: 600px) { | |||
| top: 38px; | |||
| right: 21px; | |||
| svg { | |||
| width: 18px; | |||
| } | |||
| } | |||
| `; | |||
| @@ -19,6 +19,7 @@ import useIsMobile from "../../../../hooks/useIsMobile"; | |||
| import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter"; | |||
| import { useEffect } from "react"; | |||
| import { useState } from "react"; | |||
| import ImagesCarousel from "../ImagesCarousel/ImagesCarousel"; | |||
| const OfferDetails = (props) => { | |||
| const offer = props.offer; | |||
| @@ -26,6 +27,7 @@ const OfferDetails = (props) => { | |||
| const dimension = useScreenDimensions(); | |||
| const { isMobile } = useIsMobile(); | |||
| const [images, setImages] = useState([]); | |||
| const [imagesCarouselModal, setImagesCarouselModal] = useState(false); | |||
| useEffect(() => { | |||
| if (props?.offer?.offer?.images) { | |||
| @@ -51,18 +53,40 @@ const OfferDetails = (props) => { | |||
| } | |||
| }, [props?.offer?.offer?.images]); | |||
| const date = formatDateLocale(new Date(offer?.offer?._created)); | |||
| const onModalClose = () => { | |||
| setImagesCarouselModal(false); | |||
| }; | |||
| return ( | |||
| <Details | |||
| hasScrollBar={!props.showPublishButton} | |||
| exchange={props.showExchangeButton} | |||
| singleOffer={props.singleOffer} | |||
| previewCard={props.previewCard} | |||
| > | |||
| {dimension.width <= 600 || !props.singleOffer ? ( | |||
| <ScrollerHorizontal> | |||
| {props?.offer?.offer?.images.map((item, index) => { | |||
| if (!item) return; | |||
| return ( | |||
| <> | |||
| <Details | |||
| hasScrollBar={!props.showPublishButton} | |||
| exchange={props.showExchangeButton} | |||
| singleOffer={props.singleOffer} | |||
| previewCard={props.previewCard} | |||
| > | |||
| {dimension.width <= 600 || !props.singleOffer ? ( | |||
| <ScrollerHorizontal> | |||
| {props?.offer?.offer?.images.map((item, index) => { | |||
| if (!item) return; | |||
| return ( | |||
| <OfferImage | |||
| src={ | |||
| props.createOffer | |||
| ? images[index] | |||
| : getImageUrl(item, variants.offerCard, isMobile) | |||
| } | |||
| key={item} | |||
| previewCard={props.previewCard} | |||
| onClick={() => | |||
| !props.previewCard && setImagesCarouselModal(true) | |||
| } | |||
| /> | |||
| ); | |||
| })} | |||
| </ScrollerHorizontal> | |||
| ) : ( | |||
| <ScrollerVertical> | |||
| {props?.offer?.offer?.images.map((item, index) => ( | |||
| <OfferImage | |||
| src={ | |||
| props.createOffer | |||
| @@ -70,47 +94,41 @@ const OfferDetails = (props) => { | |||
| : getImageUrl(item, variants.offerCard, isMobile) | |||
| } | |||
| key={item} | |||
| previewCard={props.previewCard} | |||
| onClick={() => | |||
| !props.previewCard && setImagesCarouselModal(true) | |||
| } | |||
| /> | |||
| ); | |||
| })} | |||
| </ScrollerHorizontal> | |||
| ) : ( | |||
| <ScrollerVertical> | |||
| {props?.offer?.offer?.images.map((item, index) => ( | |||
| <OfferImage | |||
| src={ | |||
| props.createOffer | |||
| ? images[index] | |||
| : getImageUrl(item, variants.offerCard, isMobile) | |||
| } | |||
| key={item} | |||
| /> | |||
| ))} | |||
| </ScrollerVertical> | |||
| )} | |||
| <OfferInfoContainer | |||
| singleOffer={props.singleOffer} | |||
| previewCard={props.previewCard} | |||
| > | |||
| <OfferTitle singleOffer={props.singleOffer}> | |||
| {offer?.offer?.name} | |||
| </OfferTitle> | |||
| <OfferLittleDetails | |||
| ))} | |||
| </ScrollerVertical> | |||
| )} | |||
| <OfferInfoContainer | |||
| singleOffer={props.singleOffer} | |||
| previewCard={props.previewCard} | |||
| > | |||
| <OfferDescriptionTitle> | |||
| {t("itemDetailsCard.description")} | |||
| </OfferDescriptionTitle> | |||
| <OfferDescriptionText showBarterButton={props.showExchangeButton}> | |||
| {offer?.offer?.description} | |||
| </OfferDescriptionText> | |||
| <DesciprtionPostDate previewCard={props.previewCard}> | |||
| {date} | |||
| </DesciprtionPostDate> | |||
| </OfferLittleDetails> | |||
| </OfferInfoContainer> | |||
| </Details> | |||
| <OfferTitle singleOffer={props.singleOffer}> | |||
| {offer?.offer?.name} | |||
| </OfferTitle> | |||
| <OfferLittleDetails | |||
| singleOffer={props.singleOffer} | |||
| previewCard={props.previewCard} | |||
| > | |||
| <OfferDescriptionTitle> | |||
| {t("itemDetailsCard.description")} | |||
| </OfferDescriptionTitle> | |||
| <OfferDescriptionText showBarterButton={props.showExchangeButton}> | |||
| {offer?.offer?.description} | |||
| </OfferDescriptionText> | |||
| <DesciprtionPostDate previewCard={props.previewCard}> | |||
| {date} | |||
| </DesciprtionPostDate> | |||
| </OfferLittleDetails> | |||
| </OfferInfoContainer> | |||
| </Details> | |||
| {imagesCarouselModal && ( | |||
| <ImagesCarousel offer={props.offer} onModalClose={onModalClose} /> | |||
| )} | |||
| </> | |||
| ); | |||
| }; | |||
| @@ -163,6 +163,7 @@ export const OfferImage = styled.img` | |||
| height: 144px; | |||
| margin-right: 20px; | |||
| object-fit: cover; | |||
| ${(props) => !props.previewCard && `cursor: pointer;`} | |||
| @media screen and (max-width: 600px) { | |||
| min-width: 144px; | |||
| @@ -44,6 +44,7 @@ const EditProfile = (props) => { | |||
| const dispatch = useDispatch(); | |||
| const { isMobile } = useIsMobile(); | |||
| const userId = useSelector(selectUserId); | |||
| const locations = useSelector((state) => state.locations.locations); | |||
| useEffect(() => { | |||
| setShowDetails(!isMobile); | |||
| @@ -62,10 +63,13 @@ const EditProfile = (props) => { | |||
| () => editProfileInitialValues(props?.profile), | |||
| [props?.profile] | |||
| ); | |||
| const validationSchema = useMemo(() => { | |||
| return editProfileValidation(locations); | |||
| }, []); | |||
| const formik = useFormik({ | |||
| initialValues, | |||
| validationSchema: editProfileValidation, | |||
| validationSchema: validationSchema, | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| enableReinitialize: true, | |||
| @@ -174,7 +174,7 @@ export default { | |||
| product: "Proizvod", | |||
| descriptionLabel: "Opis:", | |||
| checkButtonLabel: "Pogledaj proizvod", | |||
| offers: "Objave" | |||
| offers: "Objave", | |||
| }, | |||
| apiErrors: { | |||
| somethingWentWrong: "Greška sa serverom!", | |||
| @@ -234,6 +234,7 @@ export default { | |||
| labelLocationRequired: "Lokacija je obavezna!", | |||
| labelPhoneValid: "Unesite validan broj telefona", | |||
| labelPhoneRequired: "Broj telefona je obavezan!", | |||
| labelLocationValid: "Unesite validnu lokaciju!", | |||
| }, | |||
| deleteOffer: { | |||
| areYouSure: "Da li ste sigurni da želite da <br /> obrišete proizvod?", | |||
| @@ -390,4 +391,8 @@ export default { | |||
| description: "Opis", | |||
| email: "Mejl kompanije", | |||
| }, | |||
| carousel: { | |||
| imagesReview: "Pregled fotografija", | |||
| offer: "Proizvod:", | |||
| }, | |||
| }; | |||
| @@ -14,6 +14,7 @@ export const variants = { | |||
| deleteChat: "chatHeader", | |||
| profileCard: "profileCard", | |||
| createReviewCard: "createReviewCard", | |||
| carousel: "carousel", | |||
| }; | |||
| const cloudFlareVariants = { | |||
| @@ -33,6 +34,8 @@ const cloudFlareVariants = { | |||
| chatCardMobile: "chatCard", | |||
| profileCard: "profileCard", | |||
| createReviewCard: "primaryMobile", | |||
| carousel: "carousel", | |||
| carouselMobile: "carouselMobile", | |||
| }; | |||
| export const getImageUrl = (imageUrl, variant, isMobile = false) => { | |||
| let imageVariant = ""; | |||
| @@ -1,15 +1,19 @@ | |||
| import * as Yup from "yup"; | |||
| import i18n from "../i18n"; | |||
| export default Yup.object().shape({ | |||
| firmName: Yup.string().required(i18n.t("editProfile.labelNameRequired")), | |||
| firmPIB: Yup.string() | |||
| .required(i18n.t("editProfile.labelPIBRequired")) | |||
| .min(9, i18n.t("register.PIBnoOfCharacters")) | |||
| .max(9, i18n.t("register.PIBnoOfCharacters")), | |||
| firmLocation: Yup.string(), | |||
| firmWebsite: Yup.string(), | |||
| firmApplink: Yup.string(), | |||
| firmPhone: Yup.string() | |||
| .min(6, i18n.t("editProfile.labelPhoneValid")) | |||
| .max(14, i18n.t("editProfile.labelPhoneValid")), | |||
| }); | |||
| export default (locations) => | |||
| Yup.object().shape({ | |||
| firmName: Yup.string().required(i18n.t("editProfile.labelNameRequired")), | |||
| firmPIB: Yup.string() | |||
| .required(i18n.t("editProfile.labelPIBRequired")) | |||
| .min(9, i18n.t("register.PIBnoOfCharacters")) | |||
| .max(9, i18n.t("register.PIBnoOfCharacters")), | |||
| firmLocation: Yup.string().oneOf( | |||
| locations.map((l) => l.city), | |||
| i18n.t("editProfile.labelLocationValid") | |||
| ), | |||
| firmWebsite: Yup.string(), | |||
| firmApplink: Yup.string(), | |||
| firmPhone: Yup.string() | |||
| .min(6, i18n.t("editProfile.labelPhoneValid")) | |||
| .max(14, i18n.t("editProfile.labelPhoneValid")), | |||
| }); | |||