| @@ -1,20 +0,0 @@ | |||
| import React from 'react'; | |||
| import PropTypes from 'prop-types'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| const Auth = ({ children }) => { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <div className="c-auth"> | |||
| <h1 className="c-auth__title">{t(`login.welcome`)}</h1> | |||
| {children} | |||
| </div> | |||
| ); | |||
| }; | |||
| Auth.propTypes = { | |||
| children: PropTypes.node, | |||
| }; | |||
| export default Auth; | |||
| @@ -1,24 +0,0 @@ | |||
| import React from 'react'; | |||
| import PropTypes from 'prop-types'; | |||
| import SectionLoader from '../Loader/SectionLoader'; | |||
| const AuthCard = ({ children, title, subtitle, isLoading }) => { | |||
| return ( | |||
| <div className="c-auth-card"> | |||
| <SectionLoader isLoading={isLoading}> | |||
| <h1 className="c-auth-card__title">{title}</h1> | |||
| <h2 className="c-auth-card__subtitle">{subtitle}</h2> | |||
| {children} | |||
| </SectionLoader> | |||
| </div> | |||
| ); | |||
| }; | |||
| AuthCard.propTypes = { | |||
| children: PropTypes.node, | |||
| title: PropTypes.string, | |||
| subtitle: PropTypes.string, | |||
| isLoading: PropTypes.bool, | |||
| }; | |||
| export default AuthCard; | |||
| @@ -1,17 +1,22 @@ | |||
| import React from 'react' | |||
| import { ArrowContainer, ArrowIcon } from "./ArrowButton.styled" | |||
| import React from "react"; | |||
| import { ArrowContainer, ArrowIcon } from "./ArrowButton.styled"; | |||
| import PropTypes from "prop-types"; | |||
| export const ArrowButton = (props) => { | |||
| return <ArrowContainer onClick={props.onClick} className={props.className} disabled={props.disabled}> | |||
| <ArrowIcon side={props.side} disabled={props.disabled}/> | |||
| return ( | |||
| <ArrowContainer | |||
| onClick={props.onClick} | |||
| className={props.className} | |||
| disabled={props.disabled} | |||
| > | |||
| <ArrowIcon side={props.side} disabled={props.disabled} /> | |||
| </ArrowContainer> | |||
| } | |||
| ); | |||
| }; | |||
| ArrowButton.propTypes = { | |||
| onClick: PropTypes.func, | |||
| className: PropTypes.string, | |||
| side:PropTypes.string, | |||
| disabled:PropTypes.bool, | |||
| } | |||
| onClick: PropTypes.func, | |||
| className: PropTypes.string, | |||
| side: PropTypes.string, | |||
| disabled: PropTypes.bool, | |||
| }; | |||
| @@ -1,93 +0,0 @@ | |||
| import React, { useRef } from 'react'; | |||
| import PropType from 'prop-types'; | |||
| const Button = ({ | |||
| variant, | |||
| size, | |||
| children, | |||
| authButton, | |||
| type, | |||
| onClick, | |||
| textTransform, | |||
| className, | |||
| disabled, | |||
| hidden, | |||
| minWidth, | |||
| ...restProps | |||
| }) => { | |||
| const buttonRef = useRef(null); | |||
| function styles() { | |||
| let style = 'c-btn'; | |||
| if (variant) { | |||
| style += ` c-btn--${variant}`; | |||
| } | |||
| if (size) { | |||
| style += ` c-btn--${size}`; | |||
| } | |||
| if (textTransform) { | |||
| style += ` c-btn--${textTransform}`; | |||
| } | |||
| if (authButton) { | |||
| style += ` c-btn--auth`; | |||
| } | |||
| if (minWidth) { | |||
| style += ` c-btn--${minWidth}`; | |||
| } | |||
| if (hidden) { | |||
| style += ` c-btn--hidden`; | |||
| } | |||
| if (className) { | |||
| style += ` ${className}`; | |||
| } | |||
| return style; | |||
| } | |||
| function handleClick() { | |||
| buttonRef.current.blur(); | |||
| if (typeof onClick === 'function') { | |||
| onClick(); | |||
| } | |||
| } | |||
| return ( | |||
| <button | |||
| ref={buttonRef} | |||
| className={styles()} | |||
| onClick={handleClick} | |||
| type={type} | |||
| disabled={disabled} | |||
| {...restProps} | |||
| > | |||
| {children} | |||
| </button> | |||
| ); | |||
| }; | |||
| Button.propTypes = { | |||
| children: PropType.node, | |||
| textTransform: PropType.oneOf(['uppercase', 'capitalize']), | |||
| size: PropType.oneOf(['sm', 'md', 'lg', 'xl']), | |||
| authButton: PropType.bool, | |||
| variant: PropType.string, | |||
| type: PropType.oneOf(['button', 'submit', 'reset']), | |||
| onClick: PropType.func, | |||
| className: PropType.string, | |||
| disabled: PropType.bool, | |||
| minWidth: PropType.oneOf(['auto']), | |||
| hidden: PropType.bool, | |||
| }; | |||
| Button.defaultProps = { | |||
| type: 'button', | |||
| }; | |||
| export default Button; | |||
| @@ -1,21 +1,31 @@ | |||
| import React from 'react' | |||
| import { IconButtonContainer, IconButtonStyled } from "./IconButton.styled" | |||
| import React from "react"; | |||
| import { IconButtonContainer, IconButtonStyled } from "./IconButton.styled"; | |||
| import PropTypes from "prop-types"; | |||
| export const IconButton = (props) => { | |||
| return <IconButtonContainer style={props.containerStyle} className={props.className}> | |||
| <IconButtonStyled disabled={props.disabled} onClick={props.onClick} sx={props.style} iconcolor={props.iconColor}> | |||
| {props.children} | |||
| </IconButtonStyled> | |||
| return ( | |||
| <IconButtonContainer | |||
| style={props.containerStyle} | |||
| className={props.className} | |||
| > | |||
| <IconButtonStyled | |||
| disabled={props.disabled} | |||
| onClick={props.onClick} | |||
| sx={props.style} | |||
| iconcolor={props.iconColor} | |||
| > | |||
| {props.children} | |||
| </IconButtonStyled> | |||
| </IconButtonContainer> | |||
| } | |||
| ); | |||
| }; | |||
| IconButton.propTypes = { | |||
| children: PropTypes.node, | |||
| onClick: PropTypes.func, | |||
| containerStyle: PropTypes.any, | |||
| style: PropTypes.any, | |||
| className: PropTypes.string, | |||
| iconColor: PropTypes.string, | |||
| disabled: PropTypes.bool, | |||
| } | |||
| children: PropTypes.node, | |||
| onClick: PropTypes.func, | |||
| containerStyle: PropTypes.any, | |||
| style: PropTypes.any, | |||
| className: PropTypes.string, | |||
| iconColor: PropTypes.string, | |||
| disabled: PropTypes.bool, | |||
| }; | |||
| @@ -2,17 +2,21 @@ import { Box, IconButton } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../themes"; | |||
| export const IconButtonContainer = styled(Box)` | |||
| ` | |||
| export const IconButtonContainer = styled(Box)``; | |||
| export const IconButtonStyled = styled(IconButton)` | |||
| height: ${props => props.height ? props.height : "36px"}; | |||
| width: ${props => props.width ? props.width : "36px"}; | |||
| padding: 0; | |||
| ${props => props.iconcolor && ` | |||
| height: ${(props) => (props.height ? props.height : "36px")}; | |||
| width: ${(props) => (props.width ? props.width : "36px")}; | |||
| padding: 0; | |||
| ${(props) => | |||
| props.iconcolor && | |||
| ` | |||
| & svg path { | |||
| stroke: ${props.iconcolor}; | |||
| } | |||
| `} | |||
| border: ${props => props.border ? "1px solid " + selectedTheme.backgroundSponsoredColor : "none"} | |||
| ` | |||
| border: ${(props) => | |||
| props.border | |||
| ? "1px solid " + selectedTheme.backgroundSponsoredColor | |||
| : "none"} | |||
| `; | |||
| @@ -1,26 +0,0 @@ | |||
| import React from "react"; | |||
| import { LoginButtonContainer, LoginButtonStyled } from "./LoginButton.styled"; | |||
| import PropTypes from "prop-types"; | |||
| //Currently not in use | |||
| export const LoginButton = (props) => { | |||
| return ( | |||
| <LoginButtonContainer style={props.containerStyle}> | |||
| <LoginButtonStyled {...props} sx={props.style} variant="contained"> | |||
| Dugme | |||
| </LoginButtonStyled> | |||
| </LoginButtonContainer> | |||
| ); | |||
| }; | |||
| LoginButton.propTypes = { | |||
| children: PropTypes.node, | |||
| type: PropTypes.string, | |||
| variant: PropTypes.string, | |||
| style: PropTypes.any, | |||
| containerStyle: PropTypes.any, | |||
| fullWidth: PropTypes.bool, | |||
| buttonColor: PropTypes.string, | |||
| onClick: PropTypes.func | |||
| }; | |||
| @@ -1,11 +0,0 @@ | |||
| import { Box, Button } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| export const LoginButtonContainer = styled(Box)` | |||
| ` | |||
| export const LoginButtonStyled = styled(Button)` | |||
| background-color: ${props => props.backgroundColor}; | |||
| color: ${props => props.textColor} | |||
| ` | |||
| @@ -1,34 +0,0 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| PrimaryButtonWithIconContainer, | |||
| IconStyled, | |||
| PrimaryButtonWithIconStyled, | |||
| } from "./PrimaryButtonWithIcon.styled"; | |||
| const PrimaryButtonWithIcon = (props) => { | |||
| return ( | |||
| <PrimaryButtonWithIconContainer | |||
| style={props.containerStyle} | |||
| className={props.className} | |||
| > | |||
| <PrimaryButtonWithIconStyled sx={props.style} {...props.buttonProps} onClick={props.onClick}> | |||
| <IconStyled style={props.iconStyle}>{props.icon}</IconStyled> | |||
| {props.children} | |||
| </PrimaryButtonWithIconStyled> | |||
| </PrimaryButtonWithIconContainer> | |||
| ); | |||
| }; | |||
| PrimaryButtonWithIcon.propTypes = { | |||
| children: PropTypes.node, | |||
| icon: PropTypes.node, | |||
| className: PropTypes.string, | |||
| containerStyle: PropTypes.any, | |||
| style: PropTypes.any, | |||
| iconStyle: PropTypes.any, | |||
| buttonProps: PropTypes.any, | |||
| onClick: PropTypes.func, | |||
| }; | |||
| export default PrimaryButtonWithIcon; | |||
| @@ -1,28 +0,0 @@ | |||
| import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import { Icon } from "../../Icon/Icon"; | |||
| import { PrimaryButton } from "../PrimaryButton/PrimaryButton"; | |||
| export const PrimaryButtonWithIconContainer = styled(Box)``; | |||
| export const PrimaryButtonWithIconStyled = styled(PrimaryButton)` | |||
| position: relative; | |||
| `; | |||
| export const IconStyled = styled(Icon)` | |||
| position: absolute; | |||
| padding: 0; | |||
| left: 10px; | |||
| top: 0; | |||
| bottom: 0; | |||
| margin-top: auto; | |||
| margin-bottom: auto; | |||
| line-height: 21px; | |||
| & span { | |||
| position: absolute; | |||
| top: 0; | |||
| bottom: 0; | |||
| margin-top: auto; | |||
| margin-bottom: auto; | |||
| } | |||
| `; | |||
| @@ -1,10 +1,9 @@ | |||
| import React, { useEffect, useState } from "react"; | |||
| import React, { useMemo } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| CheckButton, | |||
| OfferImage, | |||
| OfferTitle, | |||
| OfferCard, | |||
| ChatOffer, | |||
| Commands, | |||
| ChatInfo, | |||
| @@ -26,34 +25,38 @@ import { | |||
| OfferTitleMobile, | |||
| PhoneIconContainer, | |||
| PhoneIcon, | |||
| LocationIconContainer, | |||
| } from "./ChatCard.styled"; | |||
| import { ReactComponent as Location } from "../../../assets/images/svg/location.svg"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import useScreenDimensions from "../../../hooks/useScreenDimensions"; | |||
| //import { useSelector } from "react-redux"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const ChatCard = (props) => { | |||
| const history = useHistory(); | |||
| const dimensions = useScreenDimensions(); | |||
| const [isMobile, setIsMobile] = useState(dimensions.width < 600); | |||
| const chat = props.chat; | |||
| const { t } = useTranslation(); | |||
| useEffect(() => { | |||
| const resize = (e) => { | |||
| if (e.target.outerWidth < 600 && isMobile) setIsMobile(false); | |||
| else if (e.target.outerWidth > 600 && !isMobile) setIsMobile(true); | |||
| }; | |||
| window.addEventListener("resize", resize); | |||
| const chat = useMemo(() => { | |||
| return props.chat; | |||
| }, [props.chat]); | |||
| return () => window.removeEventListener("resize", resize); | |||
| }, []); | |||
| const lastMessage = useMemo(() => { | |||
| if (chat?.chat?.messages && chat?.chat?.messages?.length > 0) { | |||
| return chat.chat.messages[chat.chat.messages.length - 1]?.text; | |||
| } | |||
| return ""; | |||
| }, [chat]); | |||
| const routeToItem = (userId) => { | |||
| history.push(`/messages/${userId}`); | |||
| const routeToItem = () => { | |||
| history.push(`/messages/${chat?.chat?._id}`); | |||
| }; | |||
| return ( | |||
| <ChatCardContainer onClick={isMobile ? () => routeToItem(chat?.chat?._id) : () => {}}> | |||
| <ChatCardContainer | |||
| onClick={ | |||
| dimensions.width < 600 ? () => routeToItem(chat?.chat?._id) : () => {} | |||
| } | |||
| > | |||
| <Col> | |||
| <UserImgWrapper> | |||
| <UserImage src={chat?.interlocutorData?.image} /> | |||
| @@ -61,38 +64,39 @@ const ChatCard = (props) => { | |||
| <ChatInfo> | |||
| <UserName>{chat?.interlocutorData?.name}</UserName> | |||
| {/* Only shows on Mobile */} | |||
| <OfferCardContainerMobile> | |||
| <OfferTextMobile>Proizvod:</OfferTextMobile> | |||
| <OfferTextMobile>{t("messages.cardProduct")}</OfferTextMobile> | |||
| <OfferTitleMobile>{chat?.offerData?.name}</OfferTitleMobile> | |||
| </OfferCardContainerMobile> | |||
| {/* ^^^^^ */} | |||
| <LastMessage> | |||
| {chat?.chat?.messages | |||
| ? chat?.chat?.messages[chat?.chat?.messages?.length - 1]?.text | |||
| : ""} | |||
| </LastMessage> | |||
| <LastMessage>{lastMessage}</LastMessage> | |||
| <LocationContainer> | |||
| <LocationIcon> | |||
| <Location height="12px" width="12px" /> | |||
| </LocationIcon> | |||
| <LocationIconContainer> | |||
| <LocationIcon /> | |||
| </LocationIconContainer> | |||
| <XSText>{chat?.interlocutorData?.location}</XSText> | |||
| </LocationContainer> | |||
| </ChatInfo> | |||
| </Col> | |||
| <Line /> | |||
| {/* Only shows on Desktop */} | |||
| <Col mobileDisappear> | |||
| <ChatOffer> | |||
| <OfferImgWrapper> | |||
| <OfferImage src={chat?.offerData?.firstImage} /> | |||
| </OfferImgWrapper> | |||
| <OfferCardContainer> | |||
| <OfferText>Proizvod:</OfferText> | |||
| <OfferText>{t("messages.cardProduct")}</OfferText> | |||
| <OfferTitle>{chat?.offerData?.name}</OfferTitle> | |||
| </OfferCardContainer> | |||
| </ChatOffer> | |||
| </Col> | |||
| {/* ^^^^^^^ */} | |||
| <Commands> | |||
| <PhoneIconContainer> | |||
| <PhoneIcon /> | |||
| @@ -102,9 +106,9 @@ const ChatCard = (props) => { | |||
| textcolor={selectedTheme.primaryPurple} | |||
| variant={"outlined"} | |||
| style={{ fontWeight: "600" }} | |||
| onClick={() => routeToItem(chat?.chat?._id)} | |||
| onClick={routeToItem} | |||
| > | |||
| Pogledaj caskanje | |||
| {t("messages.seeChats")} | |||
| </CheckButton> | |||
| </Commands> | |||
| </ChatCardContainer> | |||
| @@ -129,7 +133,7 @@ ChatCard.propTypes = { | |||
| vertical: PropTypes.bool, | |||
| chat: PropTypes.any, | |||
| }; | |||
| OfferCard.defaultProps = { | |||
| ChatCard.defaultProps = { | |||
| halfwidth: false, | |||
| sponsored: false, | |||
| }; | |||
| @@ -3,9 +3,8 @@ import styled from "styled-components"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { IconButton } from "../../Buttons/IconButton/IconButton"; | |||
| import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton"; | |||
| import { Icon } from "../../Icon/Icon"; | |||
| import { ReactComponent as Eye } from "../../../assets/images/svg/eye-striked.svg"; | |||
| import { ReactComponent as Phone } from "../../../assets/images/svg/phone.svg"; | |||
| import { ReactComponent as Location } from "../../../assets/images/svg/location.svg"; | |||
| export const ChatCardContainer = styled(Container)` | |||
| display: flex; | |||
| @@ -68,20 +67,9 @@ export const OfferImgWrapper = styled(Box)` | |||
| min-width: 72px; | |||
| max-width: 72px; | |||
| `; | |||
| export const OfferFlexContainer = styled(Container)` | |||
| display: flex; | |||
| flex-direction: row; | |||
| margin: 0; | |||
| padding: 0; | |||
| max-height: 184px; | |||
| @media (max-width: 600px) { | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| flex-direction: column; | |||
| `} | |||
| } | |||
| export const LocationIcon = styled(Location)` | |||
| height: 12px; | |||
| width: 12px; | |||
| `; | |||
| export const OfferCardContainer = styled(Container)` | |||
| @@ -106,19 +94,6 @@ export const OfferCardContainerMobile = styled(Box)` | |||
| } | |||
| `; | |||
| export const OfferInfo = styled(Box)` | |||
| display: flex; | |||
| flex: 2; | |||
| flex-direction: column; | |||
| justify-content: space-between; | |||
| margin-left: 18px; | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| margin-left: 0; | |||
| margin-top: 5px; | |||
| `} | |||
| `; | |||
| export const OfferTitle = styled(Typography)` | |||
| font-family: "Open Sans"; | |||
| flex: 1; | |||
| @@ -166,129 +141,6 @@ export const OfferTitleMobile = styled(Typography)` | |||
| } | |||
| `; | |||
| export const OfferAuthor = styled(Box)` | |||
| display: flex; | |||
| flex: 1; | |||
| flex-direction: column; | |||
| `; | |||
| export const OfferAuthorName = styled(Typography)` | |||
| font-family: "Open Sans"; | |||
| line-height: 22px; | |||
| font-size: 16px; | |||
| color: ${selectedTheme.primaryText}; | |||
| @media (max-width: 600px) { | |||
| font-size: 14px; | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| line-height: 19px; | |||
| font-size: 14px; | |||
| position: absolute; | |||
| bottom: 80px; | |||
| `} | |||
| } | |||
| `; | |||
| export const OfferLocation = styled(Typography)` | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryDarkText}; | |||
| line-height: 16px; | |||
| font-size: 12px; | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| font-size: 12px; | |||
| margin-top: 5px; | |||
| position: absolute; | |||
| bottom: 61px; | |||
| `} | |||
| `; | |||
| export const OfferDetails = styled(Box)` | |||
| display: flex; | |||
| flex-direction: row; | |||
| flex-wrap: ${(props) => (!props.halfwidth ? "no-wrap" : "wrap")}; | |||
| justify-content: start; | |||
| gap: 1rem; | |||
| @media (max-width: 650px) { | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| gap: 0; | |||
| } | |||
| `; | |||
| export const OfferCategory = styled(Box)` | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryText}; | |||
| line-height: 16px; | |||
| font-size: 12px; | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| position: absolute; | |||
| bottom: 15px; | |||
| `} | |||
| `; | |||
| export const OfferPackage = styled(Box)` | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryText}; | |||
| line-height: 16px; | |||
| font-size: 12px; | |||
| `; | |||
| export const OfferViews = styled(Box)` | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryText}; | |||
| line-height: 16px; | |||
| font-size: 12px; | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| display: none; | |||
| `} | |||
| `; | |||
| export const OfferDescriptionTitle = styled(Box)` | |||
| font-family: "Open Sans"; | |||
| font-size: 12px; | |||
| color: ${selectedTheme.primaryDarkText}; | |||
| line-height: 16px; | |||
| `; | |||
| export const OfferDescriptionText = styled(Box)` | |||
| font-family: "Open Sans"; | |||
| font-size: 16px; | |||
| color: ${selectedTheme.primaryDarkText}; | |||
| line-height: 22px; | |||
| max-width: calc(100% - 230px); | |||
| max-height: 120px; | |||
| overflow: hidden; | |||
| display: -webkit-box; | |||
| -webkit-line-clamp: 5; | |||
| -webkit-box-orient: vertical; | |||
| @media (max-width: 1500px) { | |||
| display: none; | |||
| } | |||
| `; | |||
| export const OfferDescription = styled(Box)` | |||
| flex: 3; | |||
| margin: auto 0; | |||
| padding-left: 35px; | |||
| @media (max-width: 1500px) { | |||
| display: none; | |||
| } | |||
| `; | |||
| export const DetailIcon = styled(Icon)` | |||
| & svg { | |||
| width: 14px; | |||
| position: relative; | |||
| top: -1px; | |||
| } | |||
| `; | |||
| export const DetailText = styled(Typography)` | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryText}; | |||
| line-height: 16px; | |||
| font-size: 12px; | |||
| position: relative; | |||
| top: -2px; | |||
| left: 3px; | |||
| `; | |||
| export const CheckButton = styled(PrimaryButton)` | |||
| width: 180px; | |||
| height: 48px; | |||
| @@ -338,44 +190,6 @@ export const PhoneIconContainer = styled(IconButton)` | |||
| } | |||
| } | |||
| `; | |||
| export const OfferImageContainer = styled(Box)` | |||
| min-width: 144px; | |||
| min-height: 144px; | |||
| width: 144px; | |||
| height: 144px; | |||
| @media (max-width: 600px) { | |||
| ${(props) => | |||
| !props.vertical | |||
| ? ` | |||
| min-width: 108px; | |||
| min-height: 108px; | |||
| width: 108px; | |||
| height: 108px; | |||
| ` | |||
| : `margin-top: 4px;`} | |||
| border-radius: 4px; | |||
| overflow: hidden; | |||
| box-shadow: 4px 4px 9px rgba(0, 0, 0, 0.12); | |||
| } | |||
| `; | |||
| export const OfferTitleAboveImage = styled(OfferTitle)` | |||
| padding-bottom: 12px; | |||
| padding-top: 5px; | |||
| padding-left: 1px; | |||
| display: block; | |||
| ${(props) => props.vertical && `display: none;`} | |||
| @media (min-width: 551px) { | |||
| display: none; | |||
| } | |||
| `; | |||
| export const EyeIcon = styled(Eye)` | |||
| width: 12px; | |||
| height: 11px; | |||
| @media (max-width: 600px) { | |||
| position: relative; | |||
| top: 1px !important; | |||
| } | |||
| `; | |||
| export const ChatOffer = styled(Box)` | |||
| display: flex; | |||
| @@ -409,8 +223,6 @@ export const Commands = styled(Box)` | |||
| } | |||
| `; | |||
| export const OfferCard = styled(Box)``; | |||
| export const ChatInfo = styled(Box)` | |||
| height: 100%; | |||
| display: flex; | |||
| @@ -471,7 +283,7 @@ export const LocationContainer = styled(Box)` | |||
| } | |||
| `; | |||
| export const LocationIcon = styled(Box)` | |||
| export const LocationIconContainer = styled(Box)` | |||
| height: 12px; | |||
| width: auto; | |||
| position: relative; | |||
| @@ -0,0 +1,46 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import FilterRadioDropdown from "../../FilterDropdown/Radio/FilterRadioDropdown"; | |||
| import { CategoryChosenIcon, CategoryIcon } from "./CategoryChoser.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const firstCategoryOption = { | |||
| label: "SVE KATEGORIJE", | |||
| value: { _id: 0 }, | |||
| }; | |||
| const CategoryChoser = (props) => { | |||
| const filters = props.filters; | |||
| const { t } = useTranslation(); | |||
| const handleSelectCategory = (category) => { | |||
| filters.setSelectedCategory(category); | |||
| filters.clearSelectedSubcategory(); | |||
| }; | |||
| return ( | |||
| <FilterRadioDropdown | |||
| data={[...filters?.categories]} | |||
| icon={ | |||
| filters.selectedCategory?.name ? ( | |||
| <CategoryChosenIcon /> | |||
| ) : ( | |||
| <CategoryIcon /> | |||
| ) | |||
| } | |||
| title={ | |||
| filters.selectedCategory?.name | |||
| ? filters.selectedCategory?.name | |||
| : t("filters.categories.title") | |||
| } | |||
| searchPlaceholder={t("filters.categories.placeholder")} | |||
| setSelected={handleSelectCategory} | |||
| selected={filters.selectedCategory} | |||
| firstOption={firstCategoryOption} | |||
| /> | |||
| ); | |||
| }; | |||
| CategoryChoser.propTypes = { | |||
| filters: PropTypes.any, | |||
| }; | |||
| export default CategoryChoser; | |||
| @@ -0,0 +1,8 @@ | |||
| import { ReactComponent as Category } from "../../../../../assets/images/svg/category.svg"; | |||
| import { ReactComponent as CategoryChosen } from "../../../../../assets/images/svg/category-chosen.svg"; | |||
| import styled from "styled-components" | |||
| export const CategoryChosenIcon = styled(CategoryChosen)` | |||
| ` | |||
| export const CategoryIcon = styled(Category)` | |||
| ` | |||
| @@ -0,0 +1,30 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import FilterCheckboxDropdown from "../../FilterDropdown/Checkbox/FilterCheckboxDropdown"; | |||
| import { LocationIcon } from "./LocationChoser.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const LocationChoser = (props) => { | |||
| const { t } = useTranslation(); | |||
| const filters = props.filters; | |||
| return ( | |||
| <FilterCheckboxDropdown | |||
| searchPlaceholder={t("filters.location.placeholder")} | |||
| data={[...filters.locations]} | |||
| filters={ | |||
| filters?.selectedLocations?.length > 0 | |||
| ? [...filters.selectedLocations] | |||
| : [] | |||
| } | |||
| icon={<LocationIcon />} | |||
| title={t("filters.location.title")} | |||
| setItemsSelected={filters.setSelectedLocations} | |||
| /> | |||
| ); | |||
| }; | |||
| LocationChoser.propTypes = { | |||
| filters: PropTypes.any, | |||
| }; | |||
| export default LocationChoser; | |||
| @@ -0,0 +1,5 @@ | |||
| import styled from "styled-components"; | |||
| import { ReactComponent as Location } from "../../../../../assets/images/svg/location.svg"; | |||
| export const LocationIcon = styled(Location)` | |||
| ` | |||
| @@ -0,0 +1,63 @@ | |||
| import React, { useEffect, useMemo, useState } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { SubcategoryIcon } from "./SubcategoryChoser.styled"; | |||
| import FilterRadioDropdown from "../../FilterDropdown/Radio/FilterRadioDropdown"; | |||
| import _ from "lodash"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const firstSubcategoryOption = { | |||
| label: "SVE PODKATEGORIJE", | |||
| value: { _id: 0 }, | |||
| }; | |||
| const SubcategoryChoser = (props) => { | |||
| const filters = props.filters; | |||
| const { t } = useTranslation(); | |||
| const [isOpened, setIsOpened] = useState(false); | |||
| const [isDisabled, setIsDisabled] = useState(true); | |||
| const setInitialOpen = useMemo(() => { | |||
| return _.once(() => { | |||
| setIsOpened(true); | |||
| }); | |||
| }, []); | |||
| useEffect(() => { | |||
| if (!filters.selectedCategory || filters.selectedCategory?._id === 0) { | |||
| setIsOpened(false); | |||
| setIsDisabled(true); | |||
| } else { | |||
| setIsDisabled(false); | |||
| setInitialOpen(); | |||
| } | |||
| }, [filters.selectedCategory]); | |||
| const handleOpen = () => { | |||
| setIsOpened((prevState) => !prevState); | |||
| }; | |||
| return ( | |||
| <FilterRadioDropdown | |||
| data={filters.subcategories ? [...filters.subcategories] : []} | |||
| icon={<SubcategoryIcon />} | |||
| title={ | |||
| filters.selectedSubcategory?.name | |||
| ? filters.selectedSubcategory?.name | |||
| : t("filters.subcategories.title") | |||
| } | |||
| searchPlaceholder={t("filters.subcategories.placeholder")} | |||
| setSelected={filters.setSelectedSubcategory} | |||
| selected={filters.selectedSubcategory} | |||
| open={isOpened} | |||
| disabled={isDisabled} | |||
| handleOpen={handleOpen} | |||
| firstOption={firstSubcategoryOption} | |||
| /> | |||
| ); | |||
| }; | |||
| SubcategoryChoser.propTypes = { | |||
| filters: PropTypes.any, | |||
| }; | |||
| export default SubcategoryChoser; | |||
| @@ -0,0 +1,4 @@ | |||
| import styled from "styled-components"; | |||
| import { ReactComponent as Subcategory } from "../../../../../assets/images/svg/subcategory.svg"; | |||
| export const SubcategoryIcon = styled(Subcategory)``; | |||
| @@ -1,165 +1,39 @@ | |||
| import React, { useEffect, useState } from "react"; | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| ContentContainer, | |||
| FilterCardContainer, | |||
| Footer, | |||
| Header, | |||
| Title, | |||
| } from "./FilterCard.styled"; | |||
| import { ReactComponent as Subcategory } from "../../../assets/images/svg/subcategory.svg"; | |||
| import { ReactComponent as Category } from "../../../assets/images/svg/category.svg"; | |||
| import { ReactComponent as CategoryChosen } from "../../../assets/images/svg/category-chosen.svg"; | |||
| import { ReactComponent as Location } from "../../../assets/images/svg/location.svg"; | |||
| import Link from "../../Link/Link"; | |||
| import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton"; | |||
| import FilterCheckboxDropdown from "./FilterDropdown/Checkbox/FilterCheckboxDropdown"; | |||
| import FilterRadioDropdown from "./FilterDropdown/Radio/FilterRadioDropdown"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { ContentContainer, FilterCardContainer } from "./FilterCard.styled"; | |||
| import useFilters from "../../../hooks/useFilters"; | |||
| import HeaderBack from "../../ItemDetails/Header/Header"; | |||
| import FilterHeader from "./FilterHeader/FilterHeader"; | |||
| import FilterFooter from "./FilterFooter/FilterFooter"; | |||
| import CategoryChoser from "./Choser/CategoryChoser/CategoryChoser"; | |||
| import SubcategoryChoser from "./Choser/SubcategoryChoser/SubcategoryChoser"; | |||
| import LocationChoser from "./Choser/LocationChoser/LocationChoser"; | |||
| const FilterCard = (props) => { | |||
| const { t } = useTranslation(); | |||
| const [isOpened, setIsOpened] = useState(false); | |||
| const [isDisabled, setIsDisabled] = useState(true); | |||
| const filters = useFilters(props.myOffers); | |||
| useEffect(() => { | |||
| if (!filters.selectedCategory || filters.selectedCategory?._id === 0) { | |||
| setIsOpened(false); | |||
| setIsDisabled(true); | |||
| } else { | |||
| setIsDisabled(false); | |||
| } | |||
| }, [filters.selectedCategory]); | |||
| const handleSelectCategory = (category) => { | |||
| filters.setSelectedCategory(category); | |||
| filters.setSelectedSubcategory(); | |||
| }; | |||
| const handleOpen = () => { | |||
| setIsOpened((prevState) => !prevState); | |||
| }; | |||
| const handleFilters = () => { | |||
| filters.applyFilters(); | |||
| if (props.closeResponsive) props.closeResponsive(); | |||
| }; | |||
| const clearFilters = () => { | |||
| filters.clearFilters(); | |||
| }; | |||
| return ( | |||
| <FilterCardContainer | |||
| responsiveOpen={props.responsiveOpen} | |||
| responsive={props.responsive} | |||
| myOffers={props.myOffers} | |||
| > | |||
| {/* Header title for my offers */} | |||
| {props.myOffers && <HeaderBack />} | |||
| <Header> | |||
| <Title>{t("filters.title")}</Title> | |||
| <Link | |||
| to="#" | |||
| textsize={"12px"} | |||
| font={"Open Sans"} | |||
| onClick={clearFilters} | |||
| > | |||
| {t("filters.cancel")} | |||
| </Link> | |||
| </Header> | |||
| <FilterHeader /> | |||
| <ContentContainer> | |||
| {/* Categories */} | |||
| <FilterRadioDropdown | |||
| data={[...filters?.categories]} | |||
| icon={ | |||
| filters.selectedCategory?.name ? <CategoryChosen /> : <Category /> | |||
| } | |||
| title={ | |||
| filters.selectedCategory?.name | |||
| ? filters.selectedCategory?.name | |||
| : t("filters.categories.title") | |||
| } | |||
| searchPlaceholder={t("filters.categories.placeholder")} | |||
| setSelected={handleSelectCategory} | |||
| selected={filters.selectedCategory} | |||
| firstOption={{ | |||
| label: "SVE KATEGORIJE", | |||
| value: { _id: 0 }, | |||
| }} | |||
| /> | |||
| <CategoryChoser filters={filters} /> | |||
| {/* Subcategories */} | |||
| <FilterRadioDropdown | |||
| data={filters.subcategories ? [...filters.subcategories] : []} | |||
| icon={<Subcategory />} | |||
| title={ | |||
| filters.selectedSubcategory?.name | |||
| ? filters.selectedSubcategory?.name | |||
| : t("filters.subcategories.title") | |||
| } | |||
| searchPlaceholder={t("filters.subcategories.placeholder")} | |||
| setSelected={filters.setSelectedSubcategory} | |||
| selected={filters.selectedSubcategory} | |||
| open={isOpened} | |||
| disabled={isDisabled} | |||
| handleOpen={handleOpen} | |||
| firstOption={{ | |||
| label: "SVE PODKATEGORIJE", | |||
| value: { _id: 0 }, | |||
| }} | |||
| /> | |||
| <SubcategoryChoser filters={filters} /> | |||
| {/* Locations */} | |||
| <FilterCheckboxDropdown | |||
| searchPlaceholder={t("filters.location.placeholder")} | |||
| data={[...filters.locations]} | |||
| filters={ | |||
| filters?.selectedLocations?.length > 0 | |||
| ? [...filters.selectedLocations] | |||
| : [] | |||
| } | |||
| icon={<Location />} | |||
| title={t("filters.location.title")} | |||
| setItemsSelected={filters.setSelectedLocations} | |||
| /> | |||
| <LocationChoser filters={filters} /> | |||
| </ContentContainer> | |||
| <Footer responsiveOpen={props.responsiveOpen}> | |||
| {props.responsiveOpen && ( | |||
| <PrimaryButton | |||
| variant="outlined" | |||
| fullWidth | |||
| onClick={props.closeResponsive} | |||
| textcolor={selectedTheme.primaryPurple} | |||
| font="Open Sans" | |||
| style={{ | |||
| fontWeight: "600", | |||
| fontSize: "12px", | |||
| border: "0", | |||
| textAlign: "center", | |||
| }} | |||
| > | |||
| ZATVORI | |||
| </PrimaryButton> | |||
| )} | |||
| <PrimaryButton | |||
| variant="outlined" | |||
| fullWidth | |||
| onClick={handleFilters} | |||
| textcolor={selectedTheme.primaryPurple} | |||
| font="Open Sans" | |||
| style={{ | |||
| fontWeight: "600", | |||
| fontSize: "12px", | |||
| borderColor: selectedTheme.primaryPurple, | |||
| }} | |||
| > | |||
| {t("filters.usefilters")} | |||
| </PrimaryButton> | |||
| </Footer> | |||
| <FilterFooter /> | |||
| </FilterCardContainer> | |||
| ); | |||
| }; | |||
| @@ -1,6 +1,5 @@ | |||
| import { Box, Typography } from "@mui/material"; | |||
| import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../themes"; | |||
| export const FilterCardContainer = styled(Box)` | |||
| position: fixed; | |||
| @@ -50,40 +49,6 @@ export const FilterCardContainer = styled(Box)` | |||
| margin-top: -14px; | |||
| } | |||
| `; | |||
| export const Title = styled(Typography)` | |||
| font-size: 24px; | |||
| line-height: 33px; | |||
| font-weight: 700; | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryText}; | |||
| position: relative; | |||
| `; | |||
| export const Header = styled(Box)` | |||
| display: flex; | |||
| flex-direction: row; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| margin-bottom: 1rem; | |||
| `; | |||
| export const Footer = styled(Box)` | |||
| position: "sticky"; | |||
| ${(props) => | |||
| props.responsiveOpen && | |||
| ` | |||
| flex-direction: row; | |||
| display: flex; | |||
| justify-content: space-around;`} | |||
| bottom: 0; | |||
| & div button { | |||
| height: 48px; | |||
| padding-top: 7px; | |||
| } | |||
| & div button:hover { | |||
| background-color: ${selectedTheme.primaryPurple} !important; | |||
| color: ${selectedTheme.primaryBackgroundColor} !important; | |||
| } | |||
| `; | |||
| export const ContentContainer = styled(Box)` | |||
| overflow-y: auto; | |||
| height: 100%; | |||
| @@ -101,3 +66,5 @@ export const ContentContainer = styled(Box)` | |||
| scrollbar-color: #ddd; | |||
| ${() => window.scrollbars.visible && `padding-right: 15px`}; | |||
| `; | |||
| @@ -35,11 +35,14 @@ const FilterRadioDropdown = (props) => { | |||
| }, [toSearch]); | |||
| useEffect(() => { | |||
| if (props.selected?._id !== 0 && props.selected !== null && props.selected !== undefined) { | |||
| if ( | |||
| props.selected?._id !== 0 && | |||
| props.selected !== null && | |||
| props.selected !== undefined | |||
| ) { | |||
| setIsOpened(true); | |||
| } | |||
| }, [props.selected]) | |||
| }, [props.selected]); | |||
| const handleClear = () => { | |||
| setToSearch(""); | |||
| @@ -61,7 +64,7 @@ const FilterRadioDropdown = (props) => { | |||
| toggleIconClosed={<DropdownDown />} | |||
| toggleIconOpened={<DropdownUp />} | |||
| fullWidth | |||
| open={isOpened} | |||
| open={ props?.open !== undefined ? props.open : isOpened} | |||
| disabled={props.disabled} | |||
| setIsOpened={handleOpen} | |||
| toggleIconStyles={{ | |||
| @@ -0,0 +1,61 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { FilterFooterContainer } from "./FilterFooter.styled"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { PrimaryButton } from "../../../Buttons/PrimaryButton/PrimaryButton"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import useFilters from "../../../../hooks/useFilters"; | |||
| const FilterFooter = (props) => { | |||
| const { t } = useTranslation(); | |||
| const filters = useFilters(); | |||
| const handleFilters = () => { | |||
| filters.applyFilters(); | |||
| if (props.closeResponsive) props.closeResponsive(); | |||
| }; | |||
| return ( | |||
| <FilterFooterContainer> | |||
| {props.responsiveOpen && ( | |||
| <PrimaryButton | |||
| variant="outlined" | |||
| fullWidth | |||
| onClick={props.closeResponsive} | |||
| textcolor={selectedTheme.primaryPurple} | |||
| font="Open Sans" | |||
| style={{ | |||
| fontWeight: "600", | |||
| fontSize: "12px", | |||
| border: "0", | |||
| textAlign: "center", | |||
| }} | |||
| > | |||
| {t("common.close")} | |||
| </PrimaryButton> | |||
| )} | |||
| <PrimaryButton | |||
| variant="outlined" | |||
| fullWidth | |||
| onClick={handleFilters} | |||
| textcolor={selectedTheme.primaryPurple} | |||
| font="Open Sans" | |||
| style={{ | |||
| fontWeight: "600", | |||
| fontSize: "12px", | |||
| borderColor: selectedTheme.primaryPurple, | |||
| }} | |||
| > | |||
| {t("filters.usefilters")} | |||
| </PrimaryButton> | |||
| </FilterFooterContainer> | |||
| ); | |||
| }; | |||
| (FilterFooter.propTypes = { | |||
| responsiveOpen: PropTypes.bool, | |||
| closeResponsive: PropTypes.func, | |||
| }), | |||
| (FilterFooter.defaultProps = { | |||
| responsiveOpen: false, | |||
| }); | |||
| export default FilterFooter; | |||
| @@ -0,0 +1,22 @@ | |||
| import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../themes"; | |||
| export const FilterFooterContainer = styled(Box)` | |||
| position: "sticky"; | |||
| ${(props) => | |||
| props.responsiveOpen && | |||
| ` | |||
| flex-direction: row; | |||
| display: flex; | |||
| justify-content: space-around;`} | |||
| bottom: 0; | |||
| & div button { | |||
| height: 48px; | |||
| padding-top: 7px; | |||
| } | |||
| & div button:hover { | |||
| background-color: ${selectedTheme.primaryPurple} !important; | |||
| color: ${selectedTheme.primaryBackgroundColor} !important; | |||
| } | |||
| `; | |||
| @@ -0,0 +1,28 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { FilterHeaderContainer, Title } from "./FilterHeader.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import useFilters from "../../../../hooks/useFilters"; | |||
| import Link from "../../../Link/Link"; | |||
| const FilterHeader = () => { | |||
| const filters = useFilters(); | |||
| const { t } = useTranslation(); | |||
| const clearFilters = () => { | |||
| filters.clearFilters(); | |||
| }; | |||
| return ( | |||
| <FilterHeaderContainer> | |||
| <Title>{t("filters.title")}</Title> | |||
| <Link to="#" textsize={"12px"} font={"Open Sans"} onClick={clearFilters}> | |||
| {t("filters.cancel")} | |||
| </Link> | |||
| </FilterHeaderContainer> | |||
| ); | |||
| }; | |||
| FilterHeader.propTypes = { | |||
| children: PropTypes.node, | |||
| }; | |||
| export default FilterHeader; | |||
| @@ -0,0 +1,19 @@ | |||
| import { Box, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../themes"; | |||
| export const FilterHeaderContainer = styled(Box)` | |||
| display: flex; | |||
| flex-direction: row; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| margin-bottom: 1rem; | |||
| `; | |||
| export const Title = styled(Typography)` | |||
| font-size: 24px; | |||
| line-height: 33px; | |||
| font-weight: 700; | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryText}; | |||
| position: relative; | |||
| `; | |||
| @@ -1,19 +0,0 @@ | |||
| import React from 'react'; | |||
| //import PropTypes from 'prop-types'; | |||
| //import SectionLoader from '../Loader/SectionLoader'; | |||
| import { Box } from '@mui/system'; | |||
| const HomeListCard = () => { | |||
| return ( | |||
| <Box> Title </Box> | |||
| ); | |||
| }; | |||
| // AuthCard.propTypes = { | |||
| // children: PropTypes.node, | |||
| // title: PropTypes.string, | |||
| // subtitle: PropTypes.string, | |||
| // isLoading: PropTypes.bool, | |||
| // }; | |||
| export default HomeListCard; | |||
| @@ -0,0 +1,29 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { InfoGroup, InfoIcon, InfoText } from "./Information.styled"; | |||
| const Information = (props) => { | |||
| return ( | |||
| <InfoGroup hide={props.hide}> | |||
| <InfoIcon | |||
| color={selectedTheme.iconStrokeColor} | |||
| component="span" | |||
| size="16px" | |||
| > | |||
| {/* <CategoryIcon width={"14px"} /> */} | |||
| {props.icon} | |||
| </InfoIcon> | |||
| {/* <InfoText>{offer?.offer?.category?.name}</InfoText> */} | |||
| <InfoText>{props.value}</InfoText> | |||
| </InfoGroup> | |||
| ); | |||
| }; | |||
| Information.propTypes = { | |||
| icon: PropTypes.node, | |||
| value: PropTypes.string, | |||
| hide: PropTypes.bool, | |||
| }; | |||
| export default Information; | |||
| @@ -0,0 +1,20 @@ | |||
| import { Box, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| export const InfoGroup = styled(Box)` | |||
| display: ${props => props.hide ? 'none' : 'flex'}; | |||
| flex-direction: row; | |||
| align-items: center; | |||
| gap: 4px; | |||
| `; | |||
| export const InfoIcon = styled(Box)` | |||
| display: flex; | |||
| align-items: center; | |||
| `; | |||
| export const InfoText = styled(Typography)` | |||
| font-family: "Open Sans"; | |||
| text-transform: capitalize; | |||
| @media (max-width: 600px) { | |||
| font-size: 12px; | |||
| } | |||
| `; | |||
| @@ -1,4 +1,4 @@ | |||
| import React, { useEffect } from "react"; | |||
| import React, { useEffect, useMemo } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| CheckButton, | |||
| @@ -6,9 +6,6 @@ import { | |||
| OfferInfo, | |||
| Info, | |||
| PostDate, | |||
| InfoIcon, | |||
| InfoText, | |||
| InfoGroup, | |||
| OfferTitle, | |||
| OfferDescriptionText, | |||
| OfferDescriptionTitle, | |||
| @@ -16,57 +13,45 @@ import { | |||
| OfferDetails, | |||
| OfferImage, | |||
| Scroller, | |||
| CategoryIcon, | |||
| SubcategoryIcon, | |||
| QuantityIcon, | |||
| EyeIcon, | |||
| } from "./ItemDetailsCard.styled"; | |||
| import { ReactComponent as Category } from "../../../assets/images/svg/category.svg"; | |||
| import { ReactComponent as Subcategory } from "../../../assets/images/svg/subcategory.svg"; | |||
| import { ReactComponent as Quantity } from "../../../assets/images/svg/quantity.svg"; | |||
| import { ReactComponent as Eye } from "../../../assets/images/svg/eye-striked.svg"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { selectLatestChats } from "../../../store/selectors/chatSelectors"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import { increaseCounter } from "../../../store/actions/counter/counterActions"; | |||
| import _ from "lodash"; | |||
| import { selectUserId } from "../../../store/selectors/loginSelectors"; | |||
| import { formatDateLocale } from "../../../util/helpers/dateHelpers"; | |||
| import { startChat } from "../../../util/helpers/chatHelper"; | |||
| import Information from "./Information/Information"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const ItemDetailsCard = (props) => { | |||
| const offer = props.offer; | |||
| const history = useHistory(); | |||
| const chats = useSelector(selectLatestChats); | |||
| const userId = useSelector(selectUserId); | |||
| const { t } = useTranslation(); | |||
| const dispatch = useDispatch(); | |||
| const dateCreated = new Date(offer?.offer?._created); | |||
| const increaseOfferCounter = useMemo(() => { | |||
| return _.once(function (id) { | |||
| dispatch(increaseCounter(id)); | |||
| }); | |||
| }, []); | |||
| useEffect(() => { | |||
| if (offer?.offer?._id) { | |||
| _.once(function () { | |||
| dispatch(increaseCounter(offer?.offer?._id)); | |||
| })(); | |||
| increaseOfferCounter(offer?.offer?._id); | |||
| } | |||
| }, [offer]); | |||
| const dayCreated = | |||
| dateCreated.getDate() < 10 | |||
| ? "0" + dateCreated.getDate() | |||
| : dateCreated.getDate(); | |||
| const monthCreated = | |||
| dateCreated.getMonth() < 10 | |||
| ? "0" + (dateCreated.getMonth() + 1) | |||
| : dateCreated.getMonth() + 1; | |||
| const yearCreated = dateCreated.getFullYear(); | |||
| const date = formatDateLocale(new Date(offer?.offer?._created)); | |||
| const startExchange = () => { | |||
| const chatItem = chats.find( | |||
| (item) => item.chat.offerId === offer?.offer?._id | |||
| ); | |||
| if (chatItem !== undefined) { | |||
| history.push(`/messages/${chatItem.chat._id}`); | |||
| } else { | |||
| if (offer?.offer?.userId !== userId) { | |||
| history.push(`/messages/newMessage`, { | |||
| offerId: offer?.offer?._id, | |||
| }); | |||
| } | |||
| } | |||
| startChat(chats, offer, userId); | |||
| }; | |||
| return ( | |||
| <ItemDetailsCardContainer | |||
| @@ -76,48 +61,21 @@ const ItemDetailsCard = (props) => { | |||
| > | |||
| <OfferInfo> | |||
| <Info> | |||
| <InfoGroup> | |||
| <InfoIcon | |||
| color={selectedTheme.iconStrokeColor} | |||
| component="span" | |||
| size="16px" | |||
| > | |||
| <Category width={"14px"} /> | |||
| </InfoIcon> | |||
| <InfoText>{offer?.offer?.category?.name}</InfoText> | |||
| </InfoGroup> | |||
| <InfoGroup> | |||
| <InfoIcon | |||
| color={selectedTheme.iconStrokeColor} | |||
| component="span" | |||
| size="16px" | |||
| > | |||
| <Subcategory width={"14px"} /> | |||
| </InfoIcon> | |||
| <InfoText>{offer?.offer?.subcategory}</InfoText> | |||
| </InfoGroup> | |||
| <InfoGroup> | |||
| <InfoIcon | |||
| color={selectedTheme.iconStrokeColor} | |||
| component="span" | |||
| size="16px" | |||
| > | |||
| <Quantity width={"22px"} height={"16px"} /> | |||
| </InfoIcon> | |||
| <InfoText>{offer?.offer?.condition}</InfoText> | |||
| </InfoGroup> | |||
| {!props.hideViews && ( | |||
| <InfoGroup views> | |||
| <InfoIcon color={"black"} component="span" size="12px" last> | |||
| <Eye width={"18px"} height={"20px"} /> | |||
| </InfoIcon> | |||
| <InfoText>{offer?.offer?.views?.count}</InfoText> | |||
| </InfoGroup> | |||
| )} | |||
| <Information | |||
| icon={<CategoryIcon />} | |||
| value={offer?.offer?.category?.name} | |||
| /> | |||
| <Information | |||
| icon={<SubcategoryIcon />} | |||
| value={offer?.offer?.subcategory} | |||
| /> | |||
| <Information | |||
| icon={<QuantityIcon />} | |||
| value={offer?.offer?.condition} | |||
| /> | |||
| <Information icon={<EyeIcon />} value={offer?.offer?.views?.count} /> | |||
| </Info> | |||
| <PostDate> | |||
| {dayCreated}.{monthCreated}.{yearCreated} | |||
| </PostDate> | |||
| <PostDate>{date}</PostDate> | |||
| </OfferInfo> | |||
| <Details | |||
| hasScrollBar={!props.showPublishButton} | |||
| @@ -130,63 +88,34 @@ const ItemDetailsCard = (props) => { | |||
| })} | |||
| </Scroller> | |||
| <OfferDetails> | |||
| <OfferDescriptionTitle>Opis:</OfferDescriptionTitle> | |||
| <OfferDescriptionTitle> | |||
| {t("itemDetailsCard.description")} | |||
| </OfferDescriptionTitle> | |||
| <OfferDescriptionText showBarterButton={props.showExchangeButton}> | |||
| {offer?.offer?.description} | |||
| </OfferDescriptionText> | |||
| </OfferDetails> | |||
| </Details> | |||
| {!props.halfwidth && props.showExchangeButton ? ( | |||
| <React.Fragment> | |||
| {!props.halfwidth && props.showExchangeButton && ( | |||
| <CheckButton | |||
| variant={props.sponsored ? "contained" : "outlined"} | |||
| buttoncolor={selectedTheme.primaryPurple} | |||
| textcolor={props.sponsored ? "white" : selectedTheme.primaryPurple} | |||
| style={{ fontWeight: "600" }} | |||
| onClick={startExchange} | |||
| > | |||
| Trampi | |||
| {t("itemDetailsCard.startExchangeButton")} | |||
| </CheckButton> | |||
| </React.Fragment> | |||
| ) : ( | |||
| <></> | |||
| )} | |||
| </ItemDetailsCardContainer> | |||
| ); | |||
| }; | |||
| ItemDetailsCard.propTypes = { | |||
| children: PropTypes.node, | |||
| id: PropTypes.number, | |||
| title: PropTypes.string, | |||
| description: PropTypes.string, | |||
| category: PropTypes.string, | |||
| subcategory: PropTypes.string, | |||
| condition: PropTypes.string, | |||
| showNumberOfViews: PropTypes.bool, | |||
| author: PropTypes.string, | |||
| location: PropTypes.string, | |||
| images: PropTypes.node, | |||
| quantity: PropTypes.number, | |||
| package: PropTypes.string, | |||
| numberOfViews: PropTypes.number, | |||
| halfwidth: PropTypes.bool, | |||
| sponsored: PropTypes.bool, | |||
| offer: PropTypes.any, | |||
| hideViews: PropTypes.bool, | |||
| showExchangeButton: PropTypes.bool, | |||
| // offer: PropTypes.shape({ | |||
| // images: PropTypes.any, | |||
| // name:PropTypes.string, | |||
| // description:PropTypes.string, | |||
| // condition:PropTypes.string, | |||
| // category:PropTypes.shape({ | |||
| // name:PropTypes.string | |||
| // }), | |||
| // location:PropTypes.shape({ | |||
| // city:PropTypes.string | |||
| // }) | |||
| // }) | |||
| showBarterButton: PropTypes.bool, | |||
| showPublishButton: PropTypes.bool, | |||
| className: PropTypes.string, | |||
| @@ -5,6 +5,10 @@ import selectedTheme from "../../../themes"; | |||
| import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton"; | |||
| import { Icon } from "../../Icon/Icon"; | |||
| import HorizontalScroller from "../../Scroller/HorizontalScroller"; | |||
| import { ReactComponent as Category } from "../../../assets/images/svg/category.svg"; | |||
| import { ReactComponent as Subcategory } from "../../../assets/images/svg/subcategory.svg"; | |||
| import { ReactComponent as Quantity } from "../../../assets/images/svg/quantity.svg"; | |||
| import { ReactComponent as Eye } from "../../../assets/images/svg/eye-striked.svg"; | |||
| export const ItemDetailsCardContainer = styled(Container)` | |||
| display: flex; | |||
| @@ -39,12 +43,6 @@ export const OfferInfo = styled(Box)` | |||
| margin: 0; | |||
| } | |||
| `; | |||
| export const InfoGroup = styled(Box)` | |||
| display: flex; | |||
| flex-direction: row; | |||
| align-items: center; | |||
| gap: 4px; | |||
| `; | |||
| export const PostDate = styled(Typography)` | |||
| font-family: "Open Sans"; | |||
| font-size: 12px; | |||
| @@ -76,17 +74,6 @@ export const Info = styled(Box)` | |||
| left: 5px; | |||
| } | |||
| `; | |||
| export const InfoIcon = styled(Box)` | |||
| display: flex; | |||
| align-items: center; | |||
| `; | |||
| export const InfoText = styled(Typography)` | |||
| font-family: "Open Sans"; | |||
| text-transform: capitalize; | |||
| @media (max-width: 600px) { | |||
| font-size: 12px; | |||
| } | |||
| `; | |||
| export const OfferTitle = styled(Typography)` | |||
| font-family: "Open Sans"; | |||
| flex: 1; | |||
| @@ -219,6 +206,7 @@ export const CheckButton = styled(PrimaryButton)` | |||
| position: absolute; | |||
| bottom: 9px; | |||
| right: 12px; | |||
| font-weight: 600; | |||
| &:hover button { | |||
| background-color: ${selectedTheme.primaryPurple} !important; | |||
| color: white !important; | |||
| @@ -267,3 +255,18 @@ export const PublishButtonContainer = styled(Box)` | |||
| justify-content: center; | |||
| margin-bottom: 30px; | |||
| `; | |||
| export const CategoryIcon = styled(Category)` | |||
| width: 14px; | |||
| ` | |||
| export const SubcategoryIcon = styled(Subcategory)` | |||
| width: 14px; | |||
| ` | |||
| export const QuantityIcon = styled(Quantity)` | |||
| width: 22px; | |||
| height: 16px; | |||
| ` | |||
| export const EyeIcon = styled(Eye)` | |||
| width: 18px; | |||
| height: 20px; | |||
| ` | |||
| @@ -0,0 +1,14 @@ | |||
| import React from 'react' | |||
| import PropTypes from 'prop-types' | |||
| const OfferDetails = () => { | |||
| return ( | |||
| <div>OfferDetails</div> | |||
| ) | |||
| } | |||
| OfferDetails.propTypes = { | |||
| offer: PropTypes.any, | |||
| } | |||
| export default OfferDetails | |||
| @@ -15,10 +15,8 @@ const MiniChatCard = (props) => { | |||
| const { t } = useTranslation(); | |||
| const history = useHistory(); | |||
| const changeChat = () => { | |||
| // if (!props.selected) { | |||
| history.push(`/messages/${props?.chat?.chat?._id}`) | |||
| // } | |||
| } | |||
| history.push(`/messages/${props?.chat?.chat?._id}`); | |||
| }; | |||
| return ( | |||
| <MiniChatCardContainer selected={props.selected} onClick={changeChat}> | |||
| <ProfileImage src={props?.chat?.interlocutorData?.image} /> | |||
| @@ -207,6 +207,7 @@ const useFilters = (myOffers) => { | |||
| // Setters | |||
| const setSelectedCategory = (payload) => { | |||
| console.log('payl: ', payload); | |||
| if (isApplied !== false) { | |||
| dispatch(setIsAppliedStatus(false)); | |||
| } | |||
| @@ -222,6 +223,9 @@ const useFilters = (myOffers) => { | |||
| dispatch(setFilteredSubcategory(payload)); | |||
| } | |||
| }; | |||
| const clearSelectedSubcategory = () => { | |||
| setSelectedSubcategory(); | |||
| } | |||
| const setSelectedLocations = (payload) => { | |||
| if (isApplied !== false) { | |||
| dispatch(setIsAppliedStatus(false)); | |||
| @@ -235,6 +239,7 @@ const useFilters = (myOffers) => { | |||
| setSelectedCategory, | |||
| selectedSubcategory, | |||
| setSelectedSubcategory, | |||
| clearSelectedSubcategory, | |||
| selectedLocations, | |||
| setSelectedLocations, | |||
| categories, | |||
| @@ -192,6 +192,7 @@ export default { | |||
| miniChatHeaderTitle: "Moje Poruke", | |||
| send: "Pošalji", | |||
| sendPlaceholder: "Poruka...", | |||
| seeChats: "Pogledaj ćaskanje" | |||
| }, | |||
| editProfile: { | |||
| website: "Web Sajt*", | |||
| @@ -210,4 +211,8 @@ export default { | |||
| cancel: "Otkaži", | |||
| delete: "Obriši", | |||
| }, | |||
| itemDetailsCard: { | |||
| description: "Opis: ", | |||
| startExchangeButton: "Trampi", | |||
| } | |||
| }; | |||
| @@ -1,6 +1,5 @@ | |||
| import React from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import Button from '../../components/Buttons/Button'; | |||
| import Section from '../../components/Section/Section'; | |||
| const NotFoundPage = () => { | |||
| @@ -12,12 +11,6 @@ const NotFoundPage = () => { | |||
| <div className="c-error-page__content"> | |||
| <h1 className="c-error-page__title">404</h1> | |||
| <p className="c-error-page__text">{t('notFound.text')}</p> | |||
| <Button | |||
| className="c-error-page__button" | |||
| variant="primary-outlined" | |||
| > | |||
| {t('notFound.goBack')} | |||
| </Button> | |||
| </div> | |||
| </Section> | |||
| </div> | |||
| @@ -24,8 +24,8 @@ export default { | |||
| getUserSecurityQuestion: "users/username/securityquestion", | |||
| confirmSecurityQuestion: "authenticate/confirm", | |||
| confirmForgotPassword: "users/passwords/reset_token", | |||
| resetPassword: "reset-password", | |||
| forgotPassword: "forgot-password", | |||
| resetPassword: "auth/reset-password", | |||
| forgotPassword: "auth/forgot-password", | |||
| refreshToken: "/auth/refresh", | |||
| generateToken: "/authenticate/generate", | |||
| authenticate: | |||
| @@ -6,7 +6,7 @@ export const attemptFetchOffers = (payload) => { | |||
| return getRequest(apiEndpoints.offers.getOffers); | |||
| }; | |||
| export const attemptFetchOneOffer = (payload) => { | |||
| const url = `${apiEndpoints.offers.getOneOffer}/${payload}/frontend`; | |||
| const url = `${apiEndpoints.offers.getOneOffer}/${payload}`; | |||
| return getRequest(url); | |||
| } | |||
| export const attemptFetchMoreOffers = (page, payload) => { | |||
| @@ -0,0 +1,16 @@ | |||
| import history from "../../store/utils/history"; | |||
| export const startChat = (chats, offer, userId) => { | |||
| const chatItem = chats.find( | |||
| (item) => item.chat.offerId === offer?.offer?._id | |||
| ); | |||
| if (chatItem !== undefined) { | |||
| history.push(`/messages/${chatItem.chat._id}`); | |||
| } else { | |||
| if (offer?.offer?.userId !== userId) { | |||
| history.push(`/messages/newMessage`, { | |||
| offerId: offer?.offer?._id, | |||
| }); | |||
| } | |||
| } | |||
| } | |||
| @@ -38,3 +38,16 @@ export function formatDateRange(dates) { | |||
| const end = formatDate(dates.end); | |||
| return i18next.t('common.date.range', { start, end }); | |||
| } | |||
| export function formatDateLocale(date) { | |||
| const dayCreated = | |||
| date.getDate() < 10 | |||
| ? "0" + date.getDate() | |||
| : date.getDate(); | |||
| const monthCreated = | |||
| date.getMonth() < 10 | |||
| ? "0" + (date.getMonth() + 1) | |||
| : date.getMonth() + 1; | |||
| const yearCreated = date.getFullYear(); | |||
| return `${dayCreated}.${monthCreated}.${yearCreated}`; | |||
| } | |||