| @@ -4,7 +4,6 @@ import { Redirect, Route, Switch } from 'react-router-dom'; | |||
| import { | |||
| LOGIN_PAGE, | |||
| HOME_PAGE, | |||
| FORGOT_PASSWORD_PAGE, | |||
| NOT_FOUND_PAGE, | |||
| ERROR_PAGE, | |||
| BASE_PAGE, | |||
| @@ -13,7 +12,9 @@ import { | |||
| REGISTER_SUCCESSFUL_PAGE, | |||
| RESET_PASSWORD_PAGE, | |||
| CREATE_OFFER_PAGE, | |||
| ITEM_DETAILS_PAGE | |||
| ITEM_DETAILS_PAGE, | |||
| FORGOT_PASSWORD_PAGE, | |||
| PROFILE_PAGE | |||
| } from './constants/pages'; | |||
| import LoginPage from './pages/LoginPage/LoginPage'; | |||
| import HomePage from './pages/HomePage/HomePageMUI'; | |||
| @@ -27,6 +28,7 @@ import RegisterSuccessful from './pages/RegisterPages/RegisterSuccessful.js/Regi | |||
| import ResetPasswordPage from './pages/ResetPasswordPage/ResetPasswordPage'; | |||
| import CreateOffer from './pages/CreateOffer/CreateOffer'; | |||
| import ItemDetailsPage from './pages/ItemDetailsPage/ItemDetailsPageMUI'; | |||
| import ProfilePage from './pages/ProfilePage/ProfilePage'; | |||
| const AppRoutes = () => { | |||
| @@ -43,6 +45,7 @@ const AppRoutes = () => { | |||
| <Route path={RESET_PASSWORD_PAGE} component={ResetPasswordPage}/> | |||
| <Route path={CREATE_OFFER_PAGE} component={CreateOffer}/> | |||
| <Route path={ITEM_DETAILS_PAGE} component={ItemDetailsPage} /> | |||
| <Route path={PROFILE_PAGE} component={ProfilePage} /> | |||
| <Route path={HOME_PAGE} component={HomePage} /> | |||
| {/* | |||
| <PrivateRoute | |||
| @@ -0,0 +1,44 @@ | |||
| import React from 'react'; | |||
| import { Redirect, Route, Switch } from 'react-router-dom'; | |||
| import { | |||
| LOGIN_PAGE, | |||
| HOME_PAGE, | |||
| NOT_FOUND_PAGE, | |||
| ERROR_PAGE, | |||
| BASE_PAGE, | |||
| } from './constants/pages'; | |||
| <<<<<<< HEAD | |||
| // import LoginPage from './pages/LoginPage/LoginPage'; | |||
| import LoginPage from './pages/LoginPage/LoginPageMUI'; | |||
| // import HomePage from './pages/HomePage/HomePage'; | |||
| import ProfilePage from './pages/ProfilePage/ProfilePage'; | |||
| ======= | |||
| import LoginPage from './pages/LoginPage/LoginPage'; | |||
| import HomePage from './pages/HomePage/HomePageMUI'; | |||
| >>>>>>> filter-offer-card | |||
| import NotFoundPage from './pages/ErrorPages/NotFoundPage'; | |||
| import ErrorPage from './pages/ErrorPages/ErrorPage'; | |||
| import PrivateRoute from './components/Router/PrivateRoute'; | |||
| const AppRoutes = () => { | |||
| return ( | |||
| <Switch> | |||
| <Route exact path={BASE_PAGE} component={LoginPage} /> | |||
| <Route exact path={LOGIN_PAGE} component={LoginPage} /> | |||
| <Route path={NOT_FOUND_PAGE} component={NotFoundPage} /> | |||
| <Route path={ERROR_PAGE} component={ErrorPage} /> | |||
| <PrivateRoute | |||
| exact | |||
| path={HOME_PAGE} | |||
| component={ProfilePage} | |||
| /> | |||
| <Redirect from="*" to={NOT_FOUND_PAGE} /> | |||
| </Switch> | |||
| )}; | |||
| export default AppRoutes; | |||
| @@ -0,0 +1,4 @@ | |||
| <svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg"> | |||
| <path d="M3.66659 2.75H18.3333C18.8195 2.75 19.2858 2.94315 19.6296 3.28697C19.9734 3.63079 20.1666 4.0971 20.1666 4.58333V10.0833C20.1666 12.5145 19.2008 14.8461 17.4817 16.5651C15.7626 18.2842 13.4311 19.25 10.9999 19.25C9.79613 19.25 8.60414 19.0129 7.49199 18.5522C6.37984 18.0916 5.36931 17.4163 4.51811 16.5651C2.79902 14.8461 1.83325 12.5145 1.83325 10.0833V4.58333C1.83325 4.0971 2.02641 3.63079 2.37022 3.28697C2.71404 2.94315 3.18035 2.75 3.66659 2.75V2.75Z" stroke="#9E9E9E" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/> | |||
| <path d="M7.33325 9.1665L10.9999 12.8332L14.6666 9.1665" stroke="#9E9E9E" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/> | |||
| </svg> | |||
| @@ -15,14 +15,13 @@ import { | |||
| Details, | |||
| OfferDetails, | |||
| OfferImage, | |||
| Scroller, | |||
| } 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 HorizontalScroller from "../../Scroller/HorizontalScroller"; | |||
| // import {ReactComponent as DummyImage1 } from "../../../assets/images/svg/dummyImages/offer-1.svg" | |||
| const ItemDetailsCard = (props) => { | |||
| @@ -60,7 +59,7 @@ const ItemDetailsCard = (props) => { | |||
| <InfoText>{offer?.offer?.condition}</InfoText> | |||
| </InfoGroup> | |||
| <InfoGroup> | |||
| <InfoIcon color={"black"} component="span" size="12px"> | |||
| <InfoIcon color={"black"} component="span" size="12px" last> | |||
| <Eye width={"18px"} height={"20px"} /> | |||
| </InfoIcon> | |||
| <InfoText>{offer?.offer?.views?.viewers?.length}</InfoText> | |||
| @@ -69,12 +68,12 @@ const ItemDetailsCard = (props) => { | |||
| <PostDate>Objavljeno: {dayCreated}.{monthCreated}.{yearCreated}</PostDate> | |||
| </OfferInfo> | |||
| <Details> | |||
| <OfferTitle>{props.offer.name}</OfferTitle> | |||
| <HorizontalScroller> | |||
| <OfferTitle>{offer?.offer?.name}</OfferTitle> | |||
| <Scroller> | |||
| {offer?.offer?.images?.map(item => ( | |||
| <OfferImage src={item} key={item} /> | |||
| ))} | |||
| </HorizontalScroller> | |||
| </Scroller> | |||
| <OfferDetails> | |||
| <OfferDescriptionTitle>Opis:</OfferDescriptionTitle> | |||
| <OfferDescriptionText>{offer?.offer?.description}</OfferDescriptionText> | |||
| @@ -4,6 +4,7 @@ import selectedTheme from "../../../themes"; | |||
| //import { IconButton } from "../../Buttons/IconButton/IconButton"; | |||
| import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton"; | |||
| import { Icon } from "../../Icon/Icon"; | |||
| import HorizontalScroller from "../../Scroller/HorizontalScroller"; | |||
| export const ItemDetailsCardContainer = styled(Container)` | |||
| display: flex; | |||
| @@ -13,11 +14,16 @@ export const ItemDetailsCardContainer = styled(Container)` | |||
| margin: 10px 0; | |||
| border: 1px solid ${selectedTheme.borderNormal}; | |||
| background-color: ${(props) => | |||
| props.sponsored === 'true' ? selectedTheme.backgroundSponsoredColor : "white"}; | |||
| props.sponsored === "true" | |||
| ? selectedTheme.backgroundSponsoredColor | |||
| : "white"}; | |||
| border-radius: 4px; | |||
| padding: 18px; | |||
| max-width: 2000px; | |||
| position: relative; | |||
| @media (max-width: 600px) { | |||
| padding-bottom: 50px; | |||
| } | |||
| `; | |||
| export const OfferImage = styled.img` | |||
| width: 144px; | |||
| @@ -29,30 +35,49 @@ export const OfferInfo = styled(Box)` | |||
| flex: 2; | |||
| flex-direction: row; | |||
| justify-content: space-between; | |||
| margin:18px 0; | |||
| margin: 18px 0; | |||
| @media (max-width: 600px) { | |||
| margin: 0; | |||
| } | |||
| `; | |||
| export const InfoGroup = styled(Box)` | |||
| display: flex; | |||
| flex-direction: row; | |||
| align-items:center; | |||
| gap:4px; | |||
| display: flex; | |||
| flex-direction: row; | |||
| align-items: center; | |||
| gap: 4px; | |||
| @media (max-width: 600px) { | |||
| /* flex: 1; */ | |||
| ${props => props.last && `flex: none;`} | |||
| } | |||
| `; | |||
| export const PostDate = styled(Typography)` | |||
| font-family: "Open Sans"; | |||
| font-size: 12px; | |||
| color: ${selectedTheme.primaryText}; | |||
| font-family: "Open Sans"; | |||
| font-size: 12px; | |||
| color: ${selectedTheme.primaryText}; | |||
| @media (max-width: 600px) { | |||
| display: none; | |||
| } | |||
| `; | |||
| export const Info = styled(Box)` | |||
| display:flex; | |||
| gap:18px; | |||
| display: flex; | |||
| gap: 18px; | |||
| @media (max-width: 600px) { | |||
| flex: 1; | |||
| gap: 0; | |||
| justify-content: space-between; | |||
| margin-bottom: 15px; | |||
| } | |||
| `; | |||
| export const InfoIcon = styled(Box)` | |||
| display:flex; | |||
| align-items:center; | |||
| 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"; | |||
| @@ -61,6 +86,9 @@ export const OfferTitle = styled(Typography)` | |||
| font-weight: 700; | |||
| font-size: 24px; | |||
| padding: 0 72px; | |||
| @media (max-width: 600px) { | |||
| padding: 0; | |||
| } | |||
| `; | |||
| export const OfferAuthor = styled(Box)` | |||
| display: flex; | |||
| @@ -85,6 +113,9 @@ export const OfferDetails = styled(Box)` | |||
| flex-wrap: ${(props) => (!props.halfwidth ? "no-wrap" : "wrap")}; | |||
| justify-content: space-between; | |||
| padding: 0 72px; | |||
| @media (max-width: 600px) { | |||
| padding: 0; | |||
| } | |||
| `; | |||
| export const OfferCategory = styled(Box)` | |||
| font-family: "Open Sans"; | |||
| @@ -112,6 +143,11 @@ export const OfferDescriptionTitle = styled(Box)` | |||
| font-size: 12px; | |||
| color: ${selectedTheme.primaryDarkText}; | |||
| line-height: 16px; | |||
| @media (max-width: 600px) { | |||
| font-size: 9px; | |||
| line-height: 13px; | |||
| } | |||
| `; | |||
| export const OfferDescriptionText = styled(Box)` | |||
| font-family: "Open Sans"; | |||
| @@ -124,6 +160,9 @@ export const OfferDescriptionText = styled(Box)` | |||
| display: -webkit-box; | |||
| -webkit-line-clamp: 5; | |||
| -webkit-box-orient: vertical; | |||
| @media (max-width: 600px) { | |||
| font-size: 14px; | |||
| } | |||
| `; | |||
| export const OfferDescription = styled(Box)` | |||
| flex: 3; | |||
| @@ -160,11 +199,19 @@ export const CheckButton = styled(PrimaryButton)` | |||
| background-color: ${selectedTheme.primaryPurple} !important; | |||
| color: white !important; | |||
| } | |||
| @media (max-width: 600px) { | |||
| height: 44px; | |||
| } | |||
| `; | |||
| export const Details = styled(Box)` | |||
| display:flex; | |||
| flex-direction:column; | |||
| gap:12px; | |||
| display: flex; | |||
| flex-direction: column; | |||
| gap: 12px; | |||
| `; | |||
| // export const OfferImage = styled.img` | |||
| // ` | |||
| export const Scroller = styled(HorizontalScroller)` | |||
| & div { | |||
| margin: 0 9px; | |||
| } | |||
| ` | |||
| @@ -33,69 +33,76 @@ const OfferCard = (props) => { | |||
| const history = useHistory(); | |||
| const routeToItem = (itemId) => { | |||
| history.push(`/proizvodi/${itemId}`) | |||
| } | |||
| history.push(`/proizvodi/${itemId}`); | |||
| }; | |||
| return ( | |||
| <React.Fragment> | |||
| <OfferCardContainer | |||
| sponsored={props.offer.pinned.toString()} | |||
| halfwidth={props.halfwidth ? 1 : 0} | |||
| > | |||
| <OfferTitleAboveImage>{props.offer.name}</OfferTitleAboveImage> | |||
| <OfferFlexContainer> | |||
| <OfferImageContainer> | |||
| <OfferImage src={props.offer.images[0]}></OfferImage> | |||
| </OfferImageContainer> | |||
| <OfferInfo> | |||
| <OfferTitle>{props.offer.name}</OfferTitle> | |||
| <OfferAuthor> | |||
| <OfferAuthorName>{props.offer.description}</OfferAuthorName> | |||
| <OfferLocation>{props.offer.location.city}</OfferLocation> | |||
| </OfferAuthor> | |||
| <OfferDetails> | |||
| <OfferCategory> | |||
| <DetailIcon color="black" component="span" size="16px"> | |||
| <Category width={"14px"} /> | |||
| </DetailIcon> | |||
| <DetailText>{props.offer.category.name}</DetailText> | |||
| </OfferCategory> | |||
| <OfferViews> | |||
| <DetailIcon color="black" component="span" size="16px"> | |||
| <EyeIcon /> | |||
| </DetailIcon> | |||
| <DetailText>{props.offer.views.viewers.length}</DetailText> | |||
| </OfferViews> | |||
| </OfferDetails> | |||
| </OfferInfo> | |||
| {!props.halfwidth ? ( | |||
| <React.Fragment> | |||
| <Line /> | |||
| <OfferDescription> | |||
| <OfferDescriptionTitle>Opis:</OfferDescriptionTitle> | |||
| <OfferDescriptionText> | |||
| {props.offer.description} | |||
| </OfferDescriptionText> | |||
| </OfferDescription> | |||
| <OfferCardContainer | |||
| vertical={props.vertical} | |||
| sponsored={ | |||
| props.pinned !== undefined | |||
| ? props.pinned.toString() | |||
| : props?.offer?.pinned.toString() | |||
| } | |||
| halfwidth={props.halfwidth ? 1 : 0} | |||
| > | |||
| <OfferTitleAboveImage vertical={props.vertical} onClick={() => routeToItem(props?.offer?._id)}>{props?.offer?.name}</OfferTitleAboveImage> | |||
| <OfferFlexContainer vertical={props.vertical}> | |||
| <OfferImageContainer vertical={props.vertical}> | |||
| <OfferImage src={props?.offer?.images[0]} vertical={props.vertical}></OfferImage> | |||
| </OfferImageContainer> | |||
| <OfferInfo vertical={props.vertical}> | |||
| <OfferTitle vertical={props.vertical} onClick={() => routeToItem(props?.offer?._id)}>{props?.offer?.name}</OfferTitle> | |||
| <OfferAuthor> | |||
| <OfferAuthorName vertical={props.vertical}>{props?.offer?.user?.company?.name}</OfferAuthorName> | |||
| <OfferLocation vertical={props.vertical}>{props?.offer?.location?.city}</OfferLocation> | |||
| </OfferAuthor> | |||
| <OfferDetails> | |||
| <OfferCategory vertical={props.vertical}> | |||
| <DetailIcon color="black" component="span" size="16px"> | |||
| <Category width={"14px"} /> | |||
| </DetailIcon> | |||
| <DetailText>{props?.offer?.category.name}</DetailText> | |||
| </OfferCategory> | |||
| <OfferViews vertical={props.vertical}> | |||
| <DetailIcon color="black" component="span" size="16px"> | |||
| <EyeIcon /> | |||
| </DetailIcon> | |||
| <DetailText>{props?.offer?.views?.viewers?.length}</DetailText> | |||
| </OfferViews> | |||
| </OfferDetails> | |||
| </OfferInfo> | |||
| {!props.halfwidth ? ( | |||
| <React.Fragment> | |||
| <Line /> | |||
| <OfferDescription> | |||
| <OfferDescriptionTitle>Opis:</OfferDescriptionTitle> | |||
| <OfferDescriptionText> | |||
| {props?.offer?.description} | |||
| </OfferDescriptionText> | |||
| </OfferDescription> | |||
| <CheckButton | |||
| variant={props.sponsored ? "contained" : "outlined"} | |||
| buttoncolor={selectedTheme.primaryPurple} | |||
| textcolor={props.sponsored ? "white" : selectedTheme.primaryPurple} | |||
| style={{fontWeight: "600"}} | |||
| onClick = {() => routeToItem(props.offer._id)} | |||
| > | |||
| Pogledaj proizvod | |||
| </CheckButton> | |||
| </React.Fragment> | |||
| ) : ( | |||
| <></> | |||
| )} | |||
| <CheckButton | |||
| variant={props.sponsored ? "contained" : "outlined"} | |||
| buttoncolor={selectedTheme.primaryPurple} | |||
| textcolor={ | |||
| props.sponsored ? "white" : selectedTheme.primaryPurple | |||
| } | |||
| style={{ fontWeight: "600" }} | |||
| onClick={() => routeToItem(props?.offer?._id)} | |||
| > | |||
| Pogledaj proizvod | |||
| </CheckButton> | |||
| </React.Fragment> | |||
| ) : ( | |||
| <></> | |||
| )} | |||
| <MessageIcon> | |||
| <Message /> | |||
| </MessageIcon> | |||
| <MessageIcon vertical={props.vertical}> | |||
| <Message /> | |||
| </MessageIcon> | |||
| {/* {props.image} | |||
| {/* {props.image} | |||
| {props.title} | |||
| {props.description} | |||
| {props.category} | |||
| @@ -104,8 +111,8 @@ const OfferCard = (props) => { | |||
| {props.quantity} | |||
| {props.package} | |||
| {props.numberOfViews} */} | |||
| </OfferFlexContainer> | |||
| </OfferCardContainer> | |||
| </OfferFlexContainer> | |||
| </OfferCardContainer> | |||
| </React.Fragment> | |||
| ); | |||
| }; | |||
| @@ -125,6 +132,8 @@ OfferCard.propTypes = { | |||
| halfwidth: PropTypes.bool, | |||
| sponsored: PropTypes.bool, | |||
| offer: PropTypes.any, | |||
| pinned: PropTypes.bool, | |||
| vertical: PropTypes.bool, | |||
| }; | |||
| OfferCard.defaultProps = { | |||
| halfwidth: false, | |||
| @@ -28,6 +28,13 @@ export const OfferCardContainer = styled(Container)` | |||
| height: 184px; | |||
| padding: 18px; | |||
| padding-top: 12px; | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| height: 330px; | |||
| width: 180px; | |||
| margin: 0 18px; | |||
| `} | |||
| } | |||
| `; | |||
| export const OfferFlexContainer = styled(Container)` | |||
| @@ -36,6 +43,13 @@ export const OfferFlexContainer = styled(Container)` | |||
| margin: 0; | |||
| padding: 0; | |||
| max-height: 184px; | |||
| @media (max-width: 600px) { | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| flex-direction: column; | |||
| `} | |||
| } | |||
| `; | |||
| export const OfferImage = styled.img` | |||
| max-width: 144px; | |||
| @@ -43,10 +57,14 @@ export const OfferImage = styled.img` | |||
| width: 144px; | |||
| height: 144px; | |||
| @media (max-width: 600px) { | |||
| max-width: 108px; | |||
| ${(props) => | |||
| !props.vertical && | |||
| ` | |||
| max-width: 108px; | |||
| max-height: 108px; | |||
| width: 108px; | |||
| height: 108px; | |||
| `} | |||
| } | |||
| `; | |||
| export const OfferInfo = styled(Box)` | |||
| @@ -55,6 +73,12 @@ export const OfferInfo = styled(Box)` | |||
| 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"; | |||
| @@ -62,9 +86,21 @@ export const OfferTitle = styled(Typography)` | |||
| color: ${selectedTheme.primaryPurple}; | |||
| font-weight: 700; | |||
| font-size: 24px; | |||
| cursor: pointer; | |||
| @media (max-width: 550px) { | |||
| font-size: 18px; | |||
| display: none; | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| display: flex; | |||
| flex: none; | |||
| position: relative; | |||
| line-height: 22px; | |||
| margin-top: 5px; | |||
| font-size: 18px; | |||
| `} | |||
| } | |||
| `; | |||
| export const OfferAuthor = styled(Box)` | |||
| @@ -79,8 +115,14 @@ export const OfferAuthorName = styled(Typography)` | |||
| color: ${selectedTheme.primaryText}; | |||
| @media (max-width: 600px) { | |||
| font-size: 14px; | |||
| position: relative; | |||
| left: -1px; | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| line-height: 19px; | |||
| font-size: 14px; | |||
| position: absolute; | |||
| bottom: 80px; | |||
| `} | |||
| } | |||
| `; | |||
| export const OfferLocation = styled(Typography)` | |||
| @@ -88,6 +130,14 @@ export const OfferLocation = styled(Typography)` | |||
| 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; | |||
| @@ -106,6 +156,12 @@ export const OfferCategory = styled(Box)` | |||
| 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"; | |||
| @@ -118,6 +174,11 @@ export const OfferViews = styled(Box)` | |||
| color: ${selectedTheme.primaryText}; | |||
| line-height: 16px; | |||
| font-size: 12px; | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| display: none; | |||
| `} | |||
| `; | |||
| export const OfferDescriptionTitle = styled(Box)` | |||
| font-family: "Open Sans"; | |||
| @@ -203,6 +264,11 @@ export const MessageIcon = styled(IconButton)` | |||
| top: 16px; | |||
| right: 16px; | |||
| padding: 0; | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| display: none; | |||
| `} | |||
| & button svg { | |||
| width: 16px; | |||
| height: 16px; | |||
| @@ -218,10 +284,14 @@ export const OfferImageContainer = styled(Box)` | |||
| width: 144px; | |||
| height: 144px; | |||
| @media (max-width: 600px) { | |||
| min-width: 108px; | |||
| min-height: 108px; | |||
| width: 108px; | |||
| height: 108px; | |||
| ${(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); | |||
| @@ -232,6 +302,7 @@ export const OfferTitleAboveImage = styled(OfferTitle)` | |||
| padding-top: 5px; | |||
| padding-left: 1px; | |||
| display: block; | |||
| ${(props) => props.vertical && `display: none;`} | |||
| @media (min-width: 551px) { | |||
| display: none; | |||
| } | |||
| @@ -47,17 +47,18 @@ import { useDispatch, useSelector } from "react-redux"; | |||
| import { selectUserId } from "../../store/selectors/loginSelectors"; | |||
| import { useSearch } from "../../hooks/useSearch"; | |||
| import { selectProfileName } from "../../store/selectors/profileSelectors"; | |||
| import { fetchProfile } from "../../store/actions/profile/profileActions"; | |||
| import { useHistory, useRouteMatch } from "react-router-dom"; | |||
| import { LOGIN_PAGE, REGISTER_PAGE } from "../../constants/pages"; | |||
| import { HOME_PAGE, LOGIN_PAGE, REGISTER_PAGE } from "../../constants/pages"; | |||
| import useFilters from "../../hooks/useFilters"; | |||
| import FilterCard from "../Cards/FilterCard/FilterCard"; | |||
| import { useQueryString } from "../../hooks/useQueryString"; | |||
| import { convertQueryStringFrontend } from "../../util/helpers/queryHelpers"; | |||
| import { fetchMineProfile } from "../../store/actions/profile/profileActions"; | |||
| const Header = () => { | |||
| const [openDrawer, setOpenDrawer] = useState(false); | |||
| const [openFilters, setOpenFilters] = useState(false); | |||
| const [showSearchBar, setShowSearchBar] = useState(true); | |||
| const [numberOfFilters, setNumberOfFilters] = useState(0); | |||
| const { t } = useTranslation(); | |||
| const theme = useTheme(); | |||
| @@ -73,10 +74,8 @@ const Header = () => { | |||
| const searchMobileRef = useRef(null); | |||
| const queryStringHook = useQueryString(); | |||
| useEffect(() => { | |||
| if (user?.length > 1) { | |||
| dispatch(fetchProfile(user)); | |||
| } | |||
| }, [user]); | |||
| dispatch(fetchMineProfile()); | |||
| }, []); | |||
| useEffect(() => { | |||
| setUserPopoverOpen(false); | |||
| setUserAnchorEl(null); | |||
| @@ -85,6 +84,13 @@ const Header = () => { | |||
| setUserAnchorEl(null); | |||
| }; | |||
| }, []); | |||
| useEffect(() => { | |||
| if (history.location.pathname !== "/home") { | |||
| setShowSearchBar(false); | |||
| } else { | |||
| setShowSearchBar(true); | |||
| } | |||
| }, [history.location.pathname]) | |||
| useEffect(() => { | |||
| setNumberOfFilters(filters.calculateFiltersChosen()); | |||
| }, [ | |||
| @@ -121,6 +127,7 @@ const Header = () => { | |||
| if ( | |||
| location.pathname === "/login" || | |||
| location.pathname === "/register" || | |||
| location.pathname === "/register/success" || | |||
| location.pathname === "/forgot-password" || | |||
| location.pathname === "/reset-password" || | |||
| location.pathname === "/" | |||
| @@ -248,6 +255,10 @@ const Header = () => { | |||
| setOpenFilters((prevState) => !prevState); | |||
| }; | |||
| const handleLogoClick = () => { | |||
| history.push(HOME_PAGE); | |||
| } | |||
| return ( | |||
| <HeaderContainer style={{ display: shouldShow ? "block" : "none" }}> | |||
| <AppBar | |||
| @@ -258,7 +269,7 @@ const Header = () => { | |||
| > | |||
| <Toolbar> | |||
| <ToolsContainer> | |||
| <LogoContainer> | |||
| <LogoContainer onClick={() => handleLogoClick()}> | |||
| <LogoHorizontal /> | |||
| </LogoContainer> | |||
| {matches && ( | |||
| @@ -422,6 +433,7 @@ const Header = () => { | |||
| </AppBar> | |||
| <SearchInputMobile | |||
| fullWidth | |||
| shouldShow={showSearchBar} | |||
| ref={searchMobileRef} | |||
| InputProps={{ | |||
| endAdornment: ( | |||
| @@ -61,6 +61,7 @@ export const LogoContainer = styled(Box)` | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| cursor: pointer; | |||
| `; | |||
| export const ToolsButtonsContainer = styled(Box)` | |||
| display: flex; | |||
| @@ -176,8 +177,9 @@ export const SearchInputMobile = styled(SearchInput)` | |||
| width: 80%; | |||
| top: 70px; | |||
| height: 46px; | |||
| left: -50px; | |||
| left: -5px; | |||
| font-family: "Open Sans"; | |||
| ${props => !props.shouldShow && `display: none;`} | |||
| & div { | |||
| background-color: white; | |||
| height: 40px; | |||
| @@ -12,14 +12,17 @@ import { | |||
| HeaderDetails, | |||
| BottomDetails, | |||
| StatusText, | |||
| PIBIcon, | |||
| } from "./ItemDetailsHeaderCard.styled"; | |||
| import { ItemDetailsHeaderContainer } from "./ItemDetailsHeaderCard.styled"; | |||
| import { ReactComponent as Category } from "../../../assets/images/svg/category.svg"; | |||
| import { ReactComponent as PIB } from "../../../assets/images/svg/pib.svg"; | |||
| import { ReactComponent as MessageColor } from "../../../assets/images/svg/mailColor.svg"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { useHistory } from "react-router-dom"; | |||
| const ItemDetailsHeaderCard = (props) => { | |||
| const history = useHistory(); | |||
| console.log("header offer: ", props.offer); | |||
| const offer = props.offer; | |||
| if (!props.offer) { | |||
| @@ -39,26 +42,28 @@ const ItemDetailsHeaderCard = (props) => { | |||
| 100 | |||
| ); | |||
| } | |||
| const handleGoProfile = () => { | |||
| history.push(`/profile/${offer?.offer?.userId}`); | |||
| } | |||
| return ( | |||
| <ItemDetailsHeaderContainer | |||
| sponsored={props.sponsored.toString()} | |||
| sponsored={offer?.offer?.pinned?.toString()} | |||
| halfwidth={props.halfwidth ? 1 : 0} | |||
| > | |||
| <HeaderTop> | |||
| <OfferImage src={offer?.companyData?.image}/> | |||
| <OfferDetails> | |||
| <OfferTitle>{offer?.companyData?.company?.name}</OfferTitle> | |||
| <OfferTitle onClick={handleGoProfile}>{offer?.companyData?.company?.name}</OfferTitle> | |||
| <DetailContainer> | |||
| <DetailIcon | |||
| <PIBIcon | |||
| color={selectedTheme.iconStrokeColor} | |||
| component="span" | |||
| size="22px" | |||
| > | |||
| <PIB width={"22px"} /> | |||
| </DetailIcon> | |||
| <PIB /> | |||
| </PIBIcon> | |||
| <DetailText>PIB - {offer?.companyData?.company?.PIB}</DetailText> | |||
| </DetailContainer> | |||
| <DetailContainer> | |||
| <DetailContainer shouldHideResponsive> | |||
| <DetailIcon | |||
| color={selectedTheme.iconStrokeColor} | |||
| component="span" | |||
| @@ -66,7 +71,7 @@ const ItemDetailsHeaderCard = (props) => { | |||
| > | |||
| <Category width={"22px"} /> | |||
| </DetailIcon> | |||
| <DetailText> | |||
| <DetailText > | |||
| {offer?.companyData?.company?.contacts?.location} | |||
| </DetailText> | |||
| </DetailContainer> | |||
| @@ -12,44 +12,58 @@ export const ItemDetailsHeaderContainer = styled(Box)` | |||
| box-sizing: border-box; | |||
| margin: 10px 0; | |||
| background-color: ${(props) => | |||
| props.sponsored === 'true' ? selectedTheme.backgroundSponsoredColor : "white"}; | |||
| props.sponsored === "true" | |||
| ? selectedTheme.backgroundSponsoredColor | |||
| : "white"}; | |||
| border-radius: 4px; | |||
| border: 1px solid ${selectedTheme.borderNormal}; | |||
| max-width: 2000px; | |||
| position: relative; | |||
| `; | |||
| export const DetailContainer = styled(Box)` | |||
| display: flex; | |||
| flex-direction: row; | |||
| align-items: center; | |||
| gap:7px; | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryText}; | |||
| line-height: 16px; | |||
| font-size: 12px; | |||
| display: flex; | |||
| flex-direction: row; | |||
| align-items: center; | |||
| gap: 7px; | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryText}; | |||
| line-height: 16px; | |||
| margin-bottom: 7px; | |||
| font-size: 12px; | |||
| @media (max-width: 600px) { | |||
| ${(props) => props.shouldHideResponsive && `display: none;`} | |||
| } | |||
| `; | |||
| export const HeaderTop = styled(Box)` | |||
| display:flex; | |||
| flex-direction: row; | |||
| padding:18px; | |||
| gap:18px; | |||
| display: flex; | |||
| flex-direction: row; | |||
| padding: 18px; | |||
| gap: 18px; | |||
| `; | |||
| export const HeaderDetails = styled(Box)` | |||
| background-color: ${selectedTheme.primaryIconBackgroundColor}; | |||
| `; | |||
| background-color: ${selectedTheme.primaryIconBackgroundColor}; | |||
| `; | |||
| export const BottomDetails = styled(Box)` | |||
| max-width:fit-content; | |||
| max-width: fit-content; | |||
| display: grid; | |||
| grid-template-columns: repeat(2, 1fr); | |||
| grid-template-rows: repeat(2, 1fr); | |||
| grid-column-gap: 12px; | |||
| grid-row-gap: 12px; | |||
| grid-row-gap: 12px; | |||
| padding: 18px; | |||
| @media (max-width: 600px) { | |||
| display: flex; | |||
| flex-direction: column; | |||
| } | |||
| `; | |||
| export const OfferImage = styled.img` | |||
| border-radius: 50%; | |||
| width: 144px; | |||
| height: 144px; | |||
| border-radius: 50%; | |||
| width: 144px; | |||
| height: 144px; | |||
| @media (max-width: 600px) { | |||
| width: 90px; | |||
| height: 90px; | |||
| } | |||
| `; | |||
| export const OfferInfo = styled(Box)` | |||
| display: flex; | |||
| @@ -59,11 +73,15 @@ export const OfferInfo = styled(Box)` | |||
| margin-left: 18px; | |||
| `; | |||
| export const OfferTitle = styled(Typography)` | |||
| margin-bottom:12px; | |||
| margin-bottom: 12px; | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryPurple}; | |||
| font-weight: 700; | |||
| font-size: 24px; | |||
| cursor: pointer; | |||
| @media (max-width: 600px) { | |||
| font-size: 18px; | |||
| } | |||
| `; | |||
| export const OfferAuthor = styled(Box)` | |||
| display: flex; | |||
| @@ -96,9 +114,12 @@ export const OfferDetails = styled(Box)` | |||
| `; | |||
| export const StatusText = styled(Grid)` | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryText}; | |||
| ` | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryText}; | |||
| @media (max-width: 600px) { | |||
| font-size: 12px; | |||
| } | |||
| `; | |||
| export const OfferCategory = styled(Box)` | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryText}; | |||
| @@ -150,8 +171,8 @@ export const Line = styled(Box)` | |||
| margin: auto 0; | |||
| `; | |||
| export const DetailIcon = styled(Icon)` | |||
| display:flex; | |||
| align-items:center; | |||
| display: flex; | |||
| align-items: center; | |||
| & svg { | |||
| width: 22px; | |||
| position: relative; | |||
| @@ -163,6 +184,9 @@ export const DetailText = styled(Typography)` | |||
| line-height: 16px; | |||
| font-size: 16px; | |||
| position: relative; | |||
| @media (max-width: 600px) { | |||
| font-size: 14px; | |||
| } | |||
| `; | |||
| export const CheckButton = styled(PrimaryButton)` | |||
| width: 180px; | |||
| @@ -185,4 +209,27 @@ export const MessageIcon = styled(IconButton)` | |||
| border-radius: 100%; | |||
| padding-top: 2px; | |||
| text-align: center; | |||
| @media (max-width: 600px) { | |||
| width: 32px; | |||
| height: 32px; | |||
| & button svg { | |||
| width: 16px; | |||
| height: 16px; | |||
| position: relative; | |||
| top: -4px; | |||
| left: -2px; | |||
| } | |||
| } | |||
| `; | |||
| export const PIBIcon = styled(DetailIcon)` | |||
| position: relative; | |||
| top: 1px; | |||
| & span svg { | |||
| width: 22px; | |||
| height: 22px; | |||
| @media (max-width: 600px) { | |||
| width: 14px; | |||
| height: 14px; | |||
| } | |||
| } | |||
| `; | |||
| @@ -31,6 +31,7 @@ export const MyPosts = () => { | |||
| useEffect(() => { | |||
| dispatch(fetchMineOffers()); | |||
| }, []); | |||
| console.log("mineOffers: ", mineOffers) | |||
| useEffect(() => { | |||
| if (mineOffers?.length > 0) { | |||
| if (mineOffers.length > 1) { | |||
| @@ -63,7 +64,7 @@ export const MyPosts = () => { | |||
| setArrayOfMineOffers([]) | |||
| } | |||
| } | |||
| }); | |||
| }, [mineOffers]); | |||
| return ( | |||
| <HeaderPopover | |||
| title={t("header.myOffers")} | |||
| @@ -3,9 +3,9 @@ import { LogoutIcon, ProfileImgPIB } from "./MyProfile.styled"; | |||
| import HeaderPopover from "../HeaderPopover/HeaderPopover"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { selectProfile } from "../../../store/selectors/profileSelectors"; | |||
| import { selectMineProfile } from "../../../store/selectors/profileSelectors"; | |||
| import { selectUserId } from "../../../store/selectors/loginSelectors"; | |||
| import { fetchProfile } from "../../../store/actions/profile/profileActions"; | |||
| import { fetchMineProfile } from "../../../store/actions/profile/profileActions"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { EyeIcon } from "../HeaderPopover/HeaderPopover.styled"; | |||
| import { logoutUser } from "../../../store/actions/login/loginActions"; | |||
| @@ -14,14 +14,14 @@ import { LOGIN_PAGE } from "../../../constants/pages"; | |||
| export const MyProfile = () => { | |||
| const { t } = useTranslation(); | |||
| const profile = useSelector(selectProfile); | |||
| const profile = useSelector(selectMineProfile); | |||
| const userId = useSelector(selectUserId); | |||
| const dispatch = useDispatch(); | |||
| const history = useHistory(); | |||
| const [profileAsArray, setProfileAsArray] = useState([]); | |||
| useEffect(() => { | |||
| if (userId?.length > 1) { | |||
| dispatch(fetchProfile(userId)); | |||
| dispatch(fetchMineProfile()); | |||
| } | |||
| }, [userId]); | |||
| useEffect(() => { | |||
| @@ -47,6 +47,9 @@ export const MyProfile = () => { | |||
| const handleLogout = () => { | |||
| dispatch(logoutUser(handleLogoutSuccess)); | |||
| }; | |||
| const seeMyProfile = () => { | |||
| history.push(`/profile/${userId}`); | |||
| } | |||
| return ( | |||
| <HeaderPopover | |||
| title={t("header.myProfile")} | |||
| @@ -54,6 +57,7 @@ export const MyProfile = () => { | |||
| buttonText={t("header.checkProfile")} | |||
| buttonIcon={<EyeIcon color={selectedTheme.iconYellowColor} />} | |||
| isProfile | |||
| buttonOnClick={() => seeMyProfile()} | |||
| secondButtonIcon={<LogoutIcon color={selectedTheme.iconYellowColor} />} | |||
| secondButtonText={"Odjavite se"} | |||
| secondButtonOnClick={handleLogout} | |||
| @@ -0,0 +1,20 @@ | |||
| import React from 'react' | |||
| import PropTypes from 'prop-types' | |||
| import { ProfileContainer } from './Profile.styled' | |||
| import ProfileCard from '../ProfileCard/ProfileCard' | |||
| import ProfileOffers from './ProfileOffers/ProfileOffers' | |||
| const Profile = () => { | |||
| return ( | |||
| <ProfileContainer> | |||
| <ProfileCard /> | |||
| <ProfileOffers /> | |||
| </ProfileContainer> | |||
| ) | |||
| } | |||
| Profile.propTypes = { | |||
| children: PropTypes.node, | |||
| } | |||
| export default Profile | |||
| @@ -0,0 +1,6 @@ | |||
| import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| export const ProfileContainer = styled(Box)` | |||
| ` | |||
| @@ -0,0 +1,153 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| DownArrow, | |||
| HeaderSelect, | |||
| HeaderTitle, | |||
| IconContainer, | |||
| OffersContainer, | |||
| OffersIcon, | |||
| OffersScroller, | |||
| ProfileOffersContainer, | |||
| SearchIcon, | |||
| SearchInput, | |||
| SelectOption, | |||
| } from "./ProfileOffers.styled"; | |||
| import { Grid } from "@mui/material"; | |||
| import { useState } from "react"; | |||
| import { sortEnum } from "../../../enums/sortEnum"; | |||
| import { useEffect } from "react"; | |||
| import { useSelector } from "react-redux"; | |||
| import OfferCard from "../../Cards/OfferCard/OfferCard"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { useRef } from "react"; | |||
| import { selectProfileOffers } from "../../../store/selectors/offersSelectors"; | |||
| import useScreenDimensions from "../../../hooks/useScreenDimensions"; | |||
| const ProfileOffers = () => { | |||
| const [sortOption, setSortOption] = useState(sortEnum.INITIAL); | |||
| const searchRef = useRef(null); | |||
| // const [toSearch, setToSearch] = useState(""); | |||
| const profileOffers = useSelector(selectProfileOffers); | |||
| const dimensions = useScreenDimensions(); | |||
| console.log(profileOffers); | |||
| const [offersToShow, setOffersToShow] = useState([]); | |||
| const { t } = useTranslation(); | |||
| useEffect(() => { | |||
| let newOffersToShow = [...offersToShow]; | |||
| if (sortOption.value === sortEnum.OLD.value) { | |||
| newOffersToShow.sort( | |||
| (a, b) => new Date(a._created) - new Date(b._created) | |||
| ); | |||
| } | |||
| if (sortOption.value === sortEnum.NEW.value) { | |||
| newOffersToShow.sort( | |||
| (a, b) => new Date(b._created) - new Date(a._created) | |||
| ); | |||
| } | |||
| if (sortOption.value === sortEnum.POPULAR.value) { | |||
| newOffersToShow.sort( | |||
| (a, b) => a.views.viewers.length - b.views.viewers.length | |||
| ); | |||
| } | |||
| setOffersToShow([...newOffersToShow]); | |||
| }, [sortOption]); | |||
| useEffect(() => { | |||
| if (profileOffers?.length > 0) setOffersToShow(profileOffers); | |||
| }, [profileOffers]); | |||
| const handleSearch = () => { | |||
| const valueToSearch = searchRef?.current?.value; | |||
| console.log(valueToSearch); | |||
| console.log(offersToShow); | |||
| let newOffersToShow = profileOffers.filter((item) => | |||
| item.name.toLowerCase().includes(valueToSearch.toLowerCase()) | |||
| ); | |||
| setOffersToShow([...newOffersToShow]); | |||
| }; | |||
| const handleChangeSelect = (event) => { | |||
| let chosenOption; | |||
| for (const sortOption in sortEnum) { | |||
| if (sortEnum[sortOption].value === event.target.value) { | |||
| chosenOption = sortEnum[sortOption]; | |||
| setSortOption(chosenOption); | |||
| } | |||
| } | |||
| }; | |||
| let listener; | |||
| const handleFocusSearch = () => { | |||
| listener = (event) => { | |||
| if (event.keyCode === 13) { | |||
| event.preventDefault(); | |||
| handleSearch(); | |||
| } | |||
| }; | |||
| searchRef.current.addEventListener("keyup", listener); | |||
| }; | |||
| const handleBlurSearch = () => { | |||
| searchRef.current.removeEventListener("keyup", listener); | |||
| }; | |||
| return ( | |||
| <ProfileOffersContainer> | |||
| <HeaderSelect | |||
| value={sortOption?.value ? sortOption.value : sortEnum.INITIAL.value} | |||
| IconComponent={DownArrow} | |||
| onChange={handleChangeSelect} | |||
| > | |||
| {Object.keys(sortEnum).map((property) => { | |||
| return ( | |||
| <SelectOption | |||
| value={sortEnum[property].value} | |||
| key={sortEnum[property].value} | |||
| > | |||
| {sortEnum[property].mainText} | |||
| </SelectOption> | |||
| ); | |||
| })} | |||
| </HeaderSelect> | |||
| <Grid | |||
| container | |||
| direction="row" | |||
| justifyContent="start" | |||
| alignItems="center" | |||
| sx={{ mb: 1.4 }} | |||
| > | |||
| <OffersIcon /> | |||
| <HeaderTitle>Moje objave</HeaderTitle> | |||
| </Grid> | |||
| <SearchInput | |||
| fullWidth | |||
| ref={searchRef} | |||
| onFocus={handleFocusSearch} | |||
| onBlur={handleBlurSearch} | |||
| // ref={searchRef} | |||
| placeholder={t("header.searchOffers")} | |||
| italicPlaceholder | |||
| InputProps={{ | |||
| endAdornment: ( | |||
| <IconContainer onClick={handleSearch}> | |||
| <SearchIcon /> | |||
| </IconContainer> | |||
| ), | |||
| }} | |||
| /> | |||
| <OffersContainer> | |||
| {dimensions.width > 600 ? ( | |||
| offersToShow.map((item) => ( | |||
| <OfferCard offer={item} key={JSON.stringify(item)} pinned /> | |||
| )) | |||
| ) : ( | |||
| <OffersScroller hideArrows> | |||
| {offersToShow.map((item) => ( | |||
| <OfferCard vertical offer={item} key={JSON.stringify(item)} pinned />))} | |||
| </OffersScroller> | |||
| )} | |||
| </OffersContainer> | |||
| </ProfileOffersContainer> | |||
| ); | |||
| }; | |||
| ProfileOffers.propTypes = { | |||
| children: PropTypes.node, | |||
| }; | |||
| export default ProfileOffers; | |||
| @@ -0,0 +1,105 @@ | |||
| import { Box, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../themes"; | |||
| import {ReactComponent as Search} from "../../../assets/images/svg/magnifying-glass.svg" | |||
| import {ReactComponent as Refresh} from "../../../assets/images/svg/refresh.svg" | |||
| import { TextField } from "../../TextFields/TextField/TextField"; | |||
| import { Icon } from "../../Icon/Icon"; | |||
| import Select from "../../Select/Select"; | |||
| import Option from "../../Select/Option/Option"; | |||
| import { ReactComponent as Down } from "../../../assets/images/svg/down-arrow.svg"; | |||
| import HorizontalScroller from "../../Scroller/HorizontalScroller"; | |||
| export const ProfileOffersContainer = styled(Box)` | |||
| width: 100%; | |||
| box-sizing: border-box; | |||
| padding: 0 50px; | |||
| margin-top: 34px; | |||
| position: relative; | |||
| @media (max-width: 600px) { | |||
| padding: 0; | |||
| } | |||
| `; | |||
| export const HeaderTitle = styled(Typography)` | |||
| font-size: 16px; | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryDarkTextThird}; | |||
| position: relative; | |||
| margin-left: 10px; | |||
| @media (max-width: 600px) { | |||
| font-size: 12px; | |||
| } | |||
| `; | |||
| export const OffersIcon = styled(Refresh)` | |||
| width: 18px; | |||
| height: 18px; | |||
| & path { | |||
| stroke: ${selectedTheme.primaryDarkTextThird}; | |||
| } | |||
| @media (max-width: 600px) { | |||
| width: 12px; | |||
| height: 12px; | |||
| } | |||
| ` | |||
| export const SearchInput = styled(TextField)` | |||
| position: relative; | |||
| top: 15px; | |||
| & div fieldset { | |||
| border-color: ${selectedTheme.primaryPurple} !important; | |||
| } | |||
| @media (max-width: 600px) { | |||
| top: 5px; | |||
| height: 46px; | |||
| & div { | |||
| background-color: white; | |||
| } | |||
| } | |||
| ` | |||
| export const SearchIcon = styled(Search)` | |||
| width: 18px; | |||
| height: 18px; | |||
| ` | |||
| export const IconContainer = styled(Icon)` | |||
| cursor: pointer; | |||
| position: relative; | |||
| top: 4px; | |||
| ` | |||
| export const HeaderSelect = styled(Select)` | |||
| width: 210px; | |||
| height: 35px; | |||
| font-family: "Open Sans"; | |||
| margin-top: 3px; | |||
| font-weight: 400; | |||
| position: absolute; | |||
| top: -8px; | |||
| right: 50px; | |||
| & div:first-child { | |||
| padding-left: 8px; | |||
| } | |||
| @media (max-width: 650px) { | |||
| width: 144px; | |||
| height: 30px; | |||
| font-size: 14px; | |||
| right: 1px; | |||
| } | |||
| `; | |||
| export const SelectOption = styled(Option)` | |||
| @media (max-width: 600px) { | |||
| height: 20px !important; | |||
| min-height: 35px; | |||
| margin: 2px; | |||
| } | |||
| `; | |||
| export const DownArrow = styled(Down)` | |||
| ` | |||
| export const OffersContainer = styled(Box)` | |||
| margin-top: 30px; | |||
| ` | |||
| export const OffersScroller = styled(HorizontalScroller)` | |||
| height: 330px; | |||
| margin-left: 0; | |||
| & div { | |||
| margin-left: 0; | |||
| } | |||
| ` | |||
| @@ -0,0 +1,202 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| EditButton, | |||
| ProfileCardWrapper, | |||
| ProfileName, | |||
| ProfilePIB, | |||
| ProfileMainInfo, | |||
| ProfileContact, | |||
| ContactItem, | |||
| ProfileStats, | |||
| StatsItem, | |||
| ProfileCardContainer, | |||
| AvatarImage, | |||
| HeaderTitle, | |||
| PocketIcon, | |||
| LocationIcon, | |||
| MailIcon, | |||
| GlobeIcon, | |||
| ProfilePIBContainer, | |||
| EditIcon, | |||
| MessageIcon, | |||
| MessageButton, | |||
| } from "./ProfileCard.styled"; | |||
| import { Grid, Stack } from "@mui/material"; | |||
| import PersonOutlineIcon from "@mui/icons-material/PersonOutline"; | |||
| import { useRouteMatch } from "react-router-dom"; | |||
| import { fetchProfile } from "../../store/actions/profile/profileActions"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { useEffect } from "react"; | |||
| import { selectProfile } from "../../store/selectors/profileSelectors"; | |||
| import { selectUserId } from "../../store/selectors/loginSelectors"; | |||
| import { useState } from "react"; | |||
| import { fetchProfileOffers } from "../../store/actions/offers/offersActions"; | |||
| const ProfileCard = () => { | |||
| const [isMyProfile, setIsMyProfile] = useState(false); | |||
| const routeMatch = useRouteMatch(); | |||
| const dispatch = useDispatch(); | |||
| const profile = useSelector(selectProfile); | |||
| const userId = useSelector(selectUserId); | |||
| const idProfile = routeMatch.params.idProfile; | |||
| console.log(idProfile); | |||
| useEffect(() => { | |||
| if (idProfile?.length > 0) { | |||
| dispatch(fetchProfile(idProfile)); | |||
| dispatch(fetchProfileOffers(idProfile)) | |||
| if (userId === idProfile) setIsMyProfile(true); | |||
| } | |||
| }, [idProfile]); | |||
| let percentOfSucceededExchanges; | |||
| if (profile?.statistics?.exchanges?.succeeded === 0) { | |||
| percentOfSucceededExchanges = 0; | |||
| } else { | |||
| percentOfSucceededExchanges = Math.ceil( | |||
| (profile?.statistics?.exchanges?.total / | |||
| profile?.statistics?.exchanges?.succeeded) * | |||
| 100 | |||
| ); | |||
| } | |||
| console.log(profile); | |||
| return ( | |||
| <> | |||
| <ProfileCardContainer> | |||
| <Grid | |||
| container | |||
| direction="row" | |||
| justifyContent="start" | |||
| alignItems="center" | |||
| sx={{ mb: 1.4 }} | |||
| > | |||
| <PersonOutlineIcon color="action" sx={{ mr: 0.9 }} /> | |||
| <HeaderTitle>Moj Profil</HeaderTitle> | |||
| </Grid> | |||
| <ProfileCardWrapper variant="outlined" isMyProfile={isMyProfile}> | |||
| {isMyProfile ? (<EditButton> | |||
| <EditIcon /> | |||
| </EditButton>) : ( | |||
| <MessageButton> | |||
| <MessageIcon /> | |||
| </MessageButton> | |||
| )} | |||
| <Grid | |||
| container | |||
| direction="column" | |||
| justifyContent="center" | |||
| alignItems="start" | |||
| > | |||
| {/* Profile Main Info */} | |||
| <ProfileMainInfo | |||
| container | |||
| direction="row" | |||
| justifyContent="start" | |||
| alignItems="start" | |||
| > | |||
| <Grid | |||
| direction="column" | |||
| justifyContent="start" | |||
| alignItems="center" | |||
| > | |||
| <AvatarImage alt="Player.rs" src={profile?.image} /> | |||
| </Grid> | |||
| <Grid | |||
| direction="column" | |||
| justifyContent="center" | |||
| alignItems="start" | |||
| sx={{ ml: 2 }} | |||
| > | |||
| <ProfileName isMyProfile={isMyProfile} variant="h5"> | |||
| {profile?.company?.name} | |||
| </ProfileName> | |||
| <ProfilePIBContainer | |||
| container | |||
| direction="row" | |||
| justifyContent="center" | |||
| alignItems="center" | |||
| > | |||
| <PocketIcon /> | |||
| <ProfilePIB isMyProfile={isMyProfile} variant="subtitle2"> | |||
| PIB: {profile?.company?.PIB} | |||
| </ProfilePIB> | |||
| </ProfilePIBContainer> | |||
| </Grid> | |||
| </ProfileMainInfo> | |||
| {/* Profile Contact */} | |||
| <ProfileContact | |||
| container | |||
| direction={{ xs: "column", sm: "row" }} | |||
| justifyContent={{ xs: "center", sm: "start" }} | |||
| alignItems={{ xs: "start", sm: "center" }} | |||
| > | |||
| <Stack direction="row"> | |||
| <LocationIcon isMyProfile={isMyProfile} /> | |||
| <ContactItem isMyProfile={isMyProfile} variant="subtitle2"> | |||
| {profile?.company?.contacts?.location} | |||
| </ContactItem> | |||
| </Stack> | |||
| <Stack direction="row"> | |||
| <MailIcon isMyProfile={isMyProfile} /> | |||
| <ContactItem isMyProfile={isMyProfile} variant="subtitle2"> | |||
| {profile?.email} | |||
| </ContactItem> | |||
| </Stack> | |||
| <Stack direction="row"> | |||
| <GlobeIcon isMyProfile={isMyProfile} /> | |||
| <ContactItem isMyProfile={isMyProfile} variant="subtitle2"> | |||
| {profile?.company?.contacts?.web} | |||
| </ContactItem> | |||
| </Stack> | |||
| </ProfileContact> | |||
| {/* Profile Stats */} | |||
| <ProfileStats | |||
| container | |||
| direction="row" | |||
| justifyContent="start" | |||
| alignItems="center" | |||
| > | |||
| <Grid | |||
| container | |||
| direction="column" | |||
| justifyContent="center" | |||
| alignItems="start" | |||
| sx={{ width: "fit-content" }} | |||
| > | |||
| <StatsItem variant="subtitle2"> | |||
| <b>{profile?.statistics?.publishes?.count}</b> objava | |||
| </StatsItem> | |||
| <StatsItem variant="subtitle2"> | |||
| <b>{percentOfSucceededExchanges}%</b> uspešna komunikacija | |||
| </StatsItem> | |||
| </Grid> | |||
| <Grid | |||
| container | |||
| direction="column" | |||
| justifyContent="center" | |||
| alignItems="start" | |||
| sx={{ width: "fit-content" }} | |||
| > | |||
| <StatsItem variant="subtitle2"> | |||
| <b>{profile?.statistics?.views?.count}</b> ukupnih pregleda | |||
| </StatsItem> | |||
| <StatsItem variant="subtitle2"> | |||
| <b>{percentOfSucceededExchanges}%</b> korektna saradnja | |||
| </StatsItem> | |||
| </Grid> | |||
| </ProfileStats> | |||
| </Grid> | |||
| </ProfileCardWrapper> | |||
| </ProfileCardContainer> | |||
| </> | |||
| ); | |||
| }; | |||
| ProfileCard.propTypes = { | |||
| children: PropTypes.node, | |||
| }; | |||
| export default ProfileCard; | |||
| @@ -0,0 +1,222 @@ | |||
| import styled from "styled-components"; | |||
| import { Card, Typography, Grid, Box } from "@mui/material"; | |||
| import selectedTheme from "../../themes"; | |||
| import { ReactComponent as Edit } from "../../assets/images/svg/edit.svg"; | |||
| import { ReactComponent as Pocket } from "../../assets/images/svg/pocket.svg"; | |||
| import { ReactComponent as Globe } from "../../assets/images/svg/globe.svg"; | |||
| import { ReactComponent as Mail } from "../../assets/images/svg/mail.svg"; | |||
| import { ReactComponent as Location } from "../../assets/images/svg/location.svg"; | |||
| // import { PRIMARY_PURPLE_COLOR, PRIMARY_YELLOW_COLOR } from '../../constants/stylesConstants'; | |||
| export const ProfileCardContainer = styled(Box)` | |||
| width: 100%; | |||
| box-sizing: border-box; | |||
| padding: 0 50px; | |||
| margin-top: 34px; | |||
| @media (max-width: 600px) { | |||
| padding: 0; | |||
| } | |||
| `; | |||
| export const EditIcon = styled(Edit)` | |||
| width: 18px; | |||
| height: 18px; | |||
| & path { | |||
| stroke: ${selectedTheme.primaryPurple}; | |||
| } | |||
| `; | |||
| export const EditButton = styled(Box)` | |||
| position: absolute; | |||
| right: 1rem; | |||
| top: 1rem; | |||
| color: ${selectedTheme.primaryPurple}; | |||
| font-weight: 900; | |||
| background: #f4f4f4; | |||
| border-radius: 360px; | |||
| padding: 0.45rem 0.45rem 0.27rem 0.57rem; | |||
| cursor: pointer; | |||
| `; | |||
| export const MessageButton = styled(EditButton)` | |||
| background: ${selectedTheme.primaryPurple}; | |||
| `; | |||
| export const ProfileCardWrapper = styled(Card)` | |||
| border: 1px solid ${selectedTheme.primaryPurple}; | |||
| background: ${(props) => | |||
| props.isMyProfile ? selectedTheme.primaryPurple : "white"}; | |||
| width: 100%; | |||
| min-width: fit-content; | |||
| padding: 1rem; | |||
| position: relative; | |||
| `; | |||
| export const ProfileName = styled(Typography)` | |||
| color: ${(props) => | |||
| props.isMyProfile | |||
| ? selectedTheme.primaryYellow | |||
| : selectedTheme.primaryPurple}; | |||
| font-weight: 700; | |||
| font-size: 24px; | |||
| font-family: "Open Sans"; | |||
| margin-bottom: 5px; | |||
| @media (max-width: 600px) { | |||
| font-size: 18px; | |||
| } | |||
| `; | |||
| export const ProfilePIB = styled(Typography)` | |||
| color: ${(props) => | |||
| props.isMyProfile ? "white" : selectedTheme.primaryDarkText}; | |||
| margin-top: 0.18rem; | |||
| font-family: "Open Sans"; | |||
| font-size: 16px; | |||
| padding-top: 1px; | |||
| @media (max-width: 600px) { | |||
| font-size: 14px; | |||
| } | |||
| `; | |||
| export const ProfilePIBContainer = styled(Grid)` | |||
| position: relative; | |||
| left: 5px; | |||
| `; | |||
| export const ProfileMainInfo = styled(Grid)``; | |||
| export const ProfileContact = styled(Grid)` | |||
| padding-top: 2rem; | |||
| padding-bottom: 2rem; | |||
| @media (max-width: 600px) { | |||
| padding-bottom: 1rem; | |||
| } | |||
| `; | |||
| export const ContactItem = styled(Typography)` | |||
| margin-right: 2rem; | |||
| margin-left: 0.4rem; | |||
| color: ${(props) => | |||
| props.isMyProfile ? "white" : selectedTheme.primaryDarkText}; | |||
| display: unset; | |||
| font-family: "Open Sans"; | |||
| letter-spacing: 0.02em; | |||
| font-size: 16px; | |||
| position: relative; | |||
| bottom: 1px; | |||
| @media (max-width: 600px) { | |||
| font-size: 14px; | |||
| bottom: 4px; | |||
| } | |||
| `; | |||
| export const StatsItem = styled(Typography)` | |||
| margin-right: 2rem; | |||
| display: unset; | |||
| margin-left: 1rem; | |||
| font-family: "Open Sans"; | |||
| font-size: 16px; | |||
| margin-bottom: 2px; | |||
| @media (max-width: 600px) { | |||
| font-size: 12px; | |||
| } | |||
| `; | |||
| export const ProfileStats = styled(Grid)` | |||
| background: ${selectedTheme.primaryDarkTextSecond}; | |||
| width: calc(100% + 2rem); | |||
| padding-top: 1.3rem; | |||
| padding-bottom: 1.3rem; | |||
| margin-bottom: -1rem; | |||
| margin-left: -1rem; | |||
| border-radius: 0 0 4px 4px; | |||
| `; | |||
| export const AvatarImage = styled.img` | |||
| min-height: 144px; | |||
| min-width: 144px; | |||
| width: 144px; | |||
| height: 144px; | |||
| border-radius: 100%; | |||
| @media (max-width: 600px) { | |||
| min-height: 90px; | |||
| min-width: 90px; | |||
| width: 90px; | |||
| height: 90px; | |||
| } | |||
| `; | |||
| export const HeaderTitle = styled(Typography)` | |||
| font-size: 16px; | |||
| font-family: "Open Sans"; | |||
| color: ${selectedTheme.primaryDarkTextThird}; | |||
| position: relative; | |||
| @media (max-width: 600px) { | |||
| font-size: 12px; | |||
| } | |||
| `; | |||
| export const PocketIcon = styled(Pocket)` | |||
| width: 22px; | |||
| height: 22px; | |||
| position: relative; | |||
| left: -5px; | |||
| top: 2px; | |||
| & path { | |||
| stroke: #b4b4b4; | |||
| } | |||
| @media (max-width: 600px) { | |||
| width: 14px; | |||
| height: 14px; | |||
| } | |||
| `; | |||
| export const MailIcon = styled(Mail)` | |||
| height: 24px; | |||
| width: 24px; | |||
| & path { | |||
| stroke: ${(props) => | |||
| props.isMyProfile | |||
| ? selectedTheme.iconMineProfileColor | |||
| : selectedTheme.iconProfileColor}; | |||
| } | |||
| @media (max-width: 600px) { | |||
| width: 14px; | |||
| height: 14px; | |||
| } | |||
| `; | |||
| export const GlobeIcon = styled(Globe)` | |||
| height: 22px; | |||
| width: 22px; | |||
| & path { | |||
| stroke: ${(props) => | |||
| props.isMyProfile | |||
| ? selectedTheme.iconMineProfileColor | |||
| : selectedTheme.iconProfileColor}; | |||
| } | |||
| @media (max-width: 600px) { | |||
| width: 14px; | |||
| height: 14px; | |||
| } | |||
| `; | |||
| export const LocationIcon = styled(Location)` | |||
| height: 22px; | |||
| width: 22px; | |||
| & path { | |||
| stroke: ${(props) => | |||
| props.isMyProfile | |||
| ? selectedTheme.iconMineProfileColor | |||
| : selectedTheme.iconProfileColor}; | |||
| } | |||
| @media (max-width: 600px) { | |||
| width: 14px; | |||
| height: 14px; | |||
| } | |||
| `; | |||
| export const MessageIcon = styled(Mail)` | |||
| width: 19.5px; | |||
| height: 19.5px; | |||
| position: relative; | |||
| right: 0.7px; | |||
| & path { | |||
| stroke: ${selectedTheme.primaryYellow}; | |||
| } | |||
| @media (max-width: 600px) { | |||
| width: 14px; | |||
| height: 14px; | |||
| right: 0.5px; | |||
| } | |||
| `; | |||
| @@ -54,9 +54,14 @@ const HorizontalScroller = (props) => { | |||
| scrollRef.current.scrollBy({ left: -50, behaviour: "smooth" }); | |||
| }; | |||
| return ( | |||
| <HorizontalScrollerContainer style={props.containerStyle}> | |||
| <ArrowButton onClick={handleLeft} disabled={isDisabledLeftButton} side={"left"}> | |||
| </ArrowButton> | |||
| <HorizontalScrollerContainer style={props.containerStyle} className={props.className}> | |||
| {!props.hideArrows && ( | |||
| <ArrowButton | |||
| onClick={handleLeft} | |||
| disabled={isDisabledLeftButton} | |||
| side={"left"} | |||
| ></ArrowButton> | |||
| )} | |||
| <ListContainer | |||
| innerRef={scrollRef} | |||
| style={props.listStyle} | |||
| @@ -64,8 +69,13 @@ const HorizontalScroller = (props) => { | |||
| > | |||
| {props.children} | |||
| </ListContainer> | |||
| <ArrowButton onClick={handleRight} disabled={isDisabledRightButton} side={"right"}> | |||
| </ArrowButton> | |||
| {!props.hideArrows && ( | |||
| <ArrowButton | |||
| onClick={handleRight} | |||
| disabled={isDisabledRightButton} | |||
| side={"right"} | |||
| ></ArrowButton> | |||
| )} | |||
| </HorizontalScrollerContainer> | |||
| ); | |||
| }; | |||
| @@ -75,6 +85,7 @@ HorizontalScroller.propTypes = { | |||
| className: PropTypes.string, | |||
| containerStyle: PropTypes.any, | |||
| listStyle: PropTypes.any, | |||
| hideArrows: PropTypes.bool, | |||
| }; | |||
| export default HorizontalScroller; | |||
| @@ -34,7 +34,7 @@ const UserReviewsCard = (props) => { | |||
| <Typography>{props.heading}</Typography> | |||
| </Grid> | |||
| <ReviewList> | |||
| {dataMockupdata.map((review) => ( | |||
| {offer?.companyData?.lastThreeReviews?.map((review) => ( | |||
| <> | |||
| <ListItem | |||
| alignItems="flex-start" | |||
| @@ -69,7 +69,7 @@ const UserReviewsCard = (props) => { | |||
| variant="body2" | |||
| color="text.primary" | |||
| > | |||
| "{review.quote}" | |||
| "{review?.quote}" | |||
| </Typography> | |||
| </Grid> | |||
| </Grid> | |||
| @@ -13,6 +13,7 @@ export const ReviewList = styled(List)` | |||
| padding: 2rem; | |||
| border-radius: 4px 0 0 4px; | |||
| height: 100%; | |||
| width: 100%; | |||
| overflow-y: auto; | |||
| &::-webkit-scrollbar { | |||
| width: 5px; | |||
| @@ -10,3 +10,4 @@ export const REGISTER_SUCCESSFUL_PAGE = "/register/success"; | |||
| export const RESET_PASSWORD_PAGE = "/reset-password/:token"; | |||
| export const CREATE_OFFER_PAGE = "/create-offer"; | |||
| export const ITEM_DETAILS_PAGE = "/proizvodi/:idProizvod"; | |||
| export const PROFILE_PAGE = "/profile/:idProfile" | |||
| @@ -3,7 +3,6 @@ import { useEffect, useState } from "react"; | |||
| // import _ from "lodash" | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import { HOME_PAGE } from "../constants/pages"; | |||
| import { setQueryString as setQueryStringSaga } from "../store/actions/queryString/queryStringActions"; | |||
| import { selectQueryString } from "../store/selectors/queryStringSelectors"; | |||
| // import useFilters from "./useFilters"; | |||
| @@ -53,7 +52,7 @@ export const useQueryString = () => { | |||
| useEffect(() => { | |||
| if (!initial) { | |||
| history.push({ | |||
| pathname: HOME_PAGE, | |||
| pathname: history.location.pathname, | |||
| search: "?" + globalQueryString, | |||
| }); | |||
| } | |||
| @@ -8,10 +8,10 @@ const ItemDetailsLayout = (props) => { | |||
| <ItemDetailsLayoutContainer> | |||
| {props.children} | |||
| <Grid container maxHeight="xl" spacing={2}> | |||
| <Content item xs={10} lg={9} xl={9.6} md={8} > | |||
| <Content item xs={12} lg={9} xl={9.6} md={8} > | |||
| {props.content} | |||
| </Content> | |||
| <RightCard item xs={2} lg={3} xl={2.4} md={4} > | |||
| <RightCard item xs={0} lg={3} xl={2.4} md={4} > | |||
| {props.rightCard} | |||
| </RightCard> | |||
| @@ -10,11 +10,19 @@ export const ItemDetailsLayoutContainer = styled(Container)` | |||
| display: flex; | |||
| flex: 1; | |||
| height: 100%; | |||
| @media (max-width: 1024px) { | |||
| padding-right: 54px; | |||
| } | |||
| @media (max-width: 600px) { | |||
| padding-left: 18px; | |||
| padding-right: 18px; | |||
| } | |||
| ` | |||
| export const RightCard = styled(Grid)` | |||
| margin-top: 30px; | |||
| border-top-right-radius: 4px; | |||
| width: 100%; | |||
| ` | |||
| export const Content = styled(Grid)` | |||
| ` | |||
| @@ -11,6 +11,9 @@ export const ProfileLayoutContainer = styled(Container)` | |||
| flex: 1; | |||
| height: 100%; | |||
| margin-top: 80px; | |||
| @media (max-width: 600px) { | |||
| margin-top: 40px; | |||
| } | |||
| ` | |||
| export const LeftCard = styled(Grid)` | |||
| @@ -21,6 +21,47 @@ const HomePage = () => { | |||
| <MainLayout leftCard={<FilterCard />} content={<MarketPlace />} /> | |||
| </HomePageContainer> | |||
| ); | |||
| }; | |||
| } | |||
| export default HomePage; | |||
| // import React from 'react'; | |||
| // import { Box } from '@mui/material'; | |||
| // import Navbar from '../../components/MUI/NavbarComponent'; | |||
| // import Modals from '../../components/MUI/Examples/ModalsExample'; | |||
| // import DataGrid from '../../components/MUI/Examples/DataGridExample'; | |||
| // import PagingSortingFiltering from '../../components/MUI/Examples/PagingSortingFilteringExample'; | |||
| // import PagingSortingFilteringServerSide from '../../components/MUI/Examples/PagingSortingFilteringExampleServerSide'; | |||
| // import RandomDataProvider from '../../context/RandomDataContext'; | |||
| // import { GridStyled } from './HomePage.styled'; | |||
| // import OfferCard from '../../components/Cards/OfferCard/OfferCard'; | |||
| // const HomePage = () => { | |||
| // return ( | |||
| // <> | |||
| // <Navbar /> | |||
| // <OfferCard /> | |||
| // <Box sx={{ mt: 4, mx: 4 }}> | |||
| // <GridStyled container spacing={2} justifyContent="center"> | |||
| // <GridStyled item xs={12} md={3}> | |||
| // <Modals /> | |||
| // </GridStyled> | |||
| // <GridStyled item xs={12} md={6}> | |||
| // <DataGrid /> | |||
| // </GridStyled> | |||
| // <GridStyled item xs={12} md={9}> | |||
| // <PagingSortingFiltering /> | |||
| // </GridStyled> | |||
| // <GridStyled item xs={12} md={9}> | |||
| // {/* Move to higher components? */} | |||
| // <RandomDataProvider> | |||
| // <PagingSortingFilteringServerSide /> | |||
| // </RandomDataProvider> | |||
| // </GridStyled> | |||
| // </GridStyled> | |||
| // </Box> | |||
| // </> | |||
| // ); | |||
| // }; | |||
| @@ -0,0 +1,30 @@ | |||
| import React from "react"; | |||
| // import Navbar from "../../components/MUI/NavbarComponent"; | |||
| import UserReviewsCard from "../../components/UserReviewsCard/UserReviewsCard"; | |||
| // import { ProfilePageWrapper } from "./ProfilePage.styled"; | |||
| import ItemDetailsLayout from "../../layouts/ItemDetailsLayout/ItemDetailsLayout"; | |||
| import { ProfilePageContainer } from "./ProfilePage.styled"; | |||
| import Profile from "../../components/Profile/Profile"; | |||
| // import FilterCard from "../../components/Cards/FilterCard/FilterCard"; | |||
| const ProfilePage = () => { | |||
| return ( | |||
| <ProfilePageContainer> | |||
| {/* <Navbar /> */} | |||
| {/* <ProfilePageWrapper | |||
| container | |||
| direction="row" | |||
| justifyContent="center" | |||
| alignItems="start" | |||
| sx={{ pt: 3 }} | |||
| > | |||
| {/* <FilterCard /> | |||
| <ProfileCard /> | |||
| <UserReviewsCard heading="Moje ocene" /> | |||
| </ProfilePageWrapper> */} | |||
| <ItemDetailsLayout content={<Profile/>} rightCard={<UserReviewsCard/>} /> | |||
| </ProfilePageContainer> | |||
| ); | |||
| }; | |||
| export default ProfilePage; | |||
| @@ -0,0 +1,15 @@ | |||
| import styled from 'styled-components'; | |||
| import { Grid, Box } from '@mui/material'; | |||
| export const ProfilePageWrapper = styled(Grid)` | |||
| background: #F4F4F4; | |||
| min-height: 100vh; | |||
| max-height: fit-content; | |||
| padding-bottom: 2rem; | |||
| `; | |||
| export const ProfilePageContainer = styled(Box)` | |||
| margin-top: 80px; | |||
| @media (max-width: 600px) { | |||
| margin-top: 40px; | |||
| } | |||
| ` | |||
| @@ -2,8 +2,8 @@ import axios from "axios"; | |||
| import queryString from "qs"; | |||
| const request = axios.create({ | |||
| baseURL: "http://192.168.88.150:3001/", | |||
| // baseURL: "http://192.168.88.176:3001/", | |||
| // baseURL: "http://192.168.88.150:3001/", | |||
| baseURL: "http://192.168.88.175:3005/", | |||
| headers: { | |||
| "Content-Type": "application/json", | |||
| }, | |||
| @@ -17,6 +17,6 @@ export const attemptFetchMoreOffers = (page, payload) => { | |||
| export const attemptAddOffer = (payload) => { | |||
| return postRequest(apiEndpoints.offers.addOffer, payload) | |||
| } | |||
| export const attemptFetchMineOffers = (payload) => { | |||
| export const attemptFetchProfileOffers = (payload) => { | |||
| return getRequest(`${apiEndpoints.offers.mineOffers}/${payload}/offers`) | |||
| } | |||
| @@ -13,6 +13,11 @@ export const OFFERS_SUCCESS = createSuccessType(OFFERS_SCOPE); | |||
| export const OFFERS_ERROR = createErrorType(OFFERS_SCOPE); | |||
| export const OFFERS_CLEAR = createClearType(OFFERS_SCOPE); | |||
| const OFFERS_PROFILE_SCOPE = "OFFERS_PROFILE_SCOPE"; | |||
| export const OFFERS_PROFILE_FETCH = createFetchType(OFFERS_PROFILE_SCOPE); | |||
| export const ONE_OFFER_FETCH = createFetchType(ONE_OFFER_SCOPE); | |||
| export const ONE_OFFER_SUCCESS = createSuccessType(ONE_OFFER_FETCH); | |||
| export const ONE_OFFER_ERROR = createErrorType(ONE_OFFER_SCOPE); | |||
| @@ -28,3 +33,4 @@ export const OFFERS_NO_MORE = "OFFERS_NO_MORE"; | |||
| export const OFFERS_SET_TOTAL = "OFFERS_SET_TOTAL"; | |||
| export const OFFERS_MINE_SET = "OFFERS_MY_ADD"; | |||
| export const OFFER_ADD = "OFFER_ADD"; | |||
| export const OFFERS_PROFILE_SET = "OFFERS_PROFILE_SET"; | |||
| @@ -9,6 +9,8 @@ import { | |||
| OFFERS_NO_MORE, | |||
| OFFERS_PINNED_ADD, | |||
| OFFERS_PINNED_SET, | |||
| OFFERS_PROFILE_FETCH, | |||
| OFFERS_PROFILE_SET, | |||
| OFFERS_SET, | |||
| OFFERS_SET_TOTAL, | |||
| OFFERS_SUCCESS, | |||
| @@ -94,3 +96,11 @@ export const setMineOffers = (payload) => ({ | |||
| type: OFFERS_MINE_SET, | |||
| payload, | |||
| }); | |||
| export const fetchProfileOffers = (payload) => ({ | |||
| type: OFFERS_PROFILE_FETCH, | |||
| payload, | |||
| }) | |||
| export const setProfileOffers = (payload) => ({ | |||
| type: OFFERS_PROFILE_SET, | |||
| payload, | |||
| }) | |||
| @@ -5,4 +5,8 @@ export const PROFILE_FETCH = createFetchType(PROFILE_SCOPE); | |||
| export const PROFILE_SUCCESS = createSuccessType(PROFILE_SCOPE); | |||
| export const PROFILE_ERROR = createErrorType(PROFILE_SCOPE); | |||
| export const PROFILE_SET = "PROFILE_SET"; | |||
| const PROFILE_MINE_SCOPE = "PROFILE_MINE_SCOPE"; | |||
| export const PROFILE_MINE_FETCH = createFetchType(PROFILE_MINE_SCOPE); | |||
| export const PROFILE_SET = "PROFILE_SET"; | |||
| export const PROFILE_MINE_SET = "PROFILE_MINE_SET"; | |||
| @@ -1,4 +1,4 @@ | |||
| import { PROFILE_ERROR, PROFILE_FETCH, PROFILE_SET, PROFILE_SUCCESS } from "./profileActionConstants"; | |||
| import { PROFILE_ERROR, PROFILE_FETCH, PROFILE_MINE_FETCH, PROFILE_MINE_SET, PROFILE_SET, PROFILE_SUCCESS } from "./profileActionConstants"; | |||
| export const fetchProfile = (payload) => ({ | |||
| type: PROFILE_FETCH, | |||
| @@ -16,4 +16,11 @@ export const fetchErrorProfile = (payload) => ({ | |||
| export const setProfile = (payload) => ({ | |||
| type: PROFILE_SET, | |||
| payload, | |||
| }) | |||
| export const setMineProfile = (payload) => ({ | |||
| type: PROFILE_MINE_SET, | |||
| payload, | |||
| }) | |||
| export const fetchMineProfile = () => ({ | |||
| type: PROFILE_MINE_FETCH, | |||
| }) | |||
| @@ -13,7 +13,7 @@ import { logoutUser, refreshUserToken } from "../actions/login/loginActions"; | |||
| // import { setUserAccessToken } from "../actions/user/userActions"; | |||
| //Change URL with .env | |||
| const baseURL = "http://192.168.88.150:3001/"; | |||
| const baseURL = "http://192.168.88.175:3005/"; | |||
| // const baseURL = "http://192.168.88.175:3005/"; | |||
| //Interceptor unique name | |||
| @@ -26,6 +26,7 @@ export default ({ dispatch }) => | |||
| const jwtToken = authScopeStringGetHelper(JWT_TOKEN); | |||
| const refresh = authScopeStringGetHelper(JWT_REFRESH_TOKEN); | |||
| if (!jwtToken || !refresh) return Promise.resolve(response); | |||
| console.log('ispod je'); | |||
| const jwtTokenDecoded = jwt.decode(jwtToken); | |||
| const refreshTokenDecoded = jwt.decode(refresh); | |||
| if (!response.headers?.Authorization) { | |||
| @@ -38,6 +39,7 @@ export default ({ dispatch }) => | |||
| } | |||
| // If access token is expired, refresh access token | |||
| if (new Date() > new Date(jwtTokenDecoded.exp * 1000)) { | |||
| console.log('untura je') | |||
| const axiosResponse = await axios.post(`${baseURL}auth/refresh`, { | |||
| token: refresh, | |||
| }); | |||
| @@ -90,4 +90,4 @@ function setIsAppliedStatus(state, {payload}) { | |||
| isApplied: payload, | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -13,6 +13,7 @@ import { | |||
| ONE_OFFER_ERROR, | |||
| ONE_OFFER_SUCCESS, | |||
| OFFERS_SET_TOTAL, | |||
| OFFERS_PROFILE_SET, | |||
| } from "../../actions/offers/offersActionConstants"; | |||
| import createReducer from "../../utils/createReducer"; | |||
| @@ -20,6 +21,7 @@ const initialState = { | |||
| offers: [], | |||
| pinnedOffers: [], | |||
| mineOffers: [], | |||
| profileOffers: [], | |||
| total: 0, | |||
| error: "", | |||
| newOffer: "", | |||
| @@ -42,7 +44,8 @@ export default createReducer( | |||
| [OFFERS_PINNED_ADD]: addPinnedOffers, | |||
| [OFFERS_PINNED_SET]: setPinnedOffers, | |||
| [OFFERS_SET_TOTAL]: setTotalOffers, | |||
| [OFFERS_MINE_SET]: setMineOffers | |||
| [OFFERS_MINE_SET]: setMineOffers, | |||
| [OFFERS_PROFILE_SET]: setProfileOffers, | |||
| }, | |||
| initialState | |||
| ); | |||
| @@ -127,3 +130,9 @@ function setMineOffers(state, action) { | |||
| mineOffers: action.payload | |||
| } | |||
| } | |||
| function setProfileOffers(state, action) { | |||
| return { | |||
| ...state, | |||
| profileOffers: action.payload | |||
| } | |||
| } | |||
| @@ -1,13 +1,15 @@ | |||
| import { PROFILE_SET } from "../../actions/profile/profileActionConstants"; | |||
| import { PROFILE_MINE_SET, PROFILE_SET } from "../../actions/profile/profileActionConstants"; | |||
| import createReducer from "../../utils/createReducer"; | |||
| const initialState = { | |||
| profile: {}, | |||
| mineProfile: {}, | |||
| }; | |||
| export default createReducer( | |||
| { | |||
| [PROFILE_SET]: setProfile, | |||
| [PROFILE_MINE_SET]: setMineProfile, | |||
| }, | |||
| initialState | |||
| ); | |||
| @@ -18,3 +20,9 @@ function setProfile(state, action) { | |||
| profile: action.payload, | |||
| }; | |||
| } | |||
| function setMineProfile(state, action) { | |||
| return { | |||
| ...state, | |||
| mineProfile: action.payload | |||
| } | |||
| } | |||
| @@ -1,9 +1,9 @@ | |||
| import { attemptAddOffer, attemptFetchOffers, attemptFetchOneOffer } from "../../request/offersRequest"; | |||
| import { OFFERS_FETCH, OFFER_ADD, ONE_OFFER_FETCH } from "../actions/offers/offersActionConstants"; | |||
| import { setOffers, setOffer } from "../actions/offers/offersActions"; | |||
| import { OFFERS_FETCH, OFFERS_PROFILE_FETCH, OFFER_ADD, ONE_OFFER_FETCH } from "../actions/offers/offersActionConstants"; | |||
| import { setOffers, setOffer, setProfileOffers } from "../actions/offers/offersActions"; | |||
| import { all, takeLatest, call, put, select } from "@redux-saga/core/effects"; | |||
| import { | |||
| attemptFetchMineOffers, | |||
| attemptFetchProfileOffers, | |||
| attemptFetchMoreOffers, | |||
| } from "../../request/offersRequest"; | |||
| import { convertQueryStringBackend } from "../../util/helpers/queryHelpers"; | |||
| @@ -86,7 +86,6 @@ function* fetchMoreOffers(payload) { | |||
| } | |||
| } | |||
| function* createOffer(payload) { | |||
| try { | |||
| const data = yield call(attemptAddOffer, payload); | |||
| @@ -112,13 +111,23 @@ function* fetchOneOffer(payload) { | |||
| function* fetchMineOffers() { | |||
| try { | |||
| const userId = yield select(selectUserId); | |||
| const data = yield call(attemptFetchMineOffers, userId); | |||
| const data = yield call(attemptFetchProfileOffers, userId); | |||
| yield put(setMineOffers(data.data)); | |||
| } catch (e) { | |||
| console.log(e); | |||
| } | |||
| } | |||
| function* fetchProfileOffers(payload) { | |||
| try { | |||
| const userId = payload.payload; | |||
| const data = yield call(attemptFetchProfileOffers, userId) | |||
| yield put (setProfileOffers(data.data)); | |||
| } catch (e) { | |||
| console.log(e); | |||
| } | |||
| } | |||
| export default function* offersSaga() { | |||
| yield all( | |||
| [ | |||
| @@ -127,5 +136,6 @@ export default function* offersSaga() { | |||
| takeLatest(ONE_OFFER_FETCH, fetchOneOffer), | |||
| takeLatest(OFFERS_FETCH_MORE, fetchMoreOffers), | |||
| takeLatest(OFFERS_MINE_FETCH, fetchMineOffers), | |||
| takeLatest(OFFERS_PROFILE_FETCH, fetchProfileOffers) | |||
| ]); | |||
| } | |||
| @@ -1,7 +1,8 @@ | |||
| import { all, call, put, takeLatest } from "@redux-saga/core/effects"; | |||
| import { all, call, put, takeLatest, select } from "@redux-saga/core/effects"; | |||
| import { attemptFetchProfile } from "../../request/profileRequest"; | |||
| import { PROFILE_FETCH } from "../actions/profile/profileActionConstants"; | |||
| import { setProfile } from "../actions/profile/profileActions"; | |||
| import { PROFILE_FETCH, PROFILE_MINE_FETCH } from "../actions/profile/profileActionConstants"; | |||
| import { setMineProfile, setProfile } from "../actions/profile/profileActions"; | |||
| import { selectUserId } from "../selectors/loginSelectors"; | |||
| function* fetchProfile(payload) { | |||
| try { | |||
| @@ -15,8 +16,21 @@ function* fetchProfile(payload) { | |||
| } | |||
| } | |||
| function* fetchMineProfile() { | |||
| try { | |||
| const userId = yield select(selectUserId); | |||
| const data = yield call(attemptFetchProfile, userId); | |||
| console.log(data); | |||
| if (data) yield put(setMineProfile(data.data)); | |||
| } | |||
| catch (e) { | |||
| console.log(e); | |||
| } | |||
| } | |||
| export default function* profileSaga() { | |||
| yield all([ | |||
| takeLatest(PROFILE_FETCH, fetchProfile) | |||
| takeLatest(PROFILE_FETCH, fetchProfile), | |||
| takeLatest(PROFILE_MINE_FETCH, fetchMineProfile) | |||
| ]) | |||
| } | |||
| @@ -32,3 +32,7 @@ export const selectMineOffers = createSelector( | |||
| offersSelector, | |||
| (state) => state.mineOffers | |||
| ) | |||
| export const selectProfileOffers = createSelector( | |||
| offersSelector, | |||
| (state) => state.profileOffers | |||
| ) | |||
| @@ -1,12 +1,16 @@ | |||
| import { createSelector } from "reselect"; | |||
| const profileSelector = (state) => state.profile.profile; | |||
| const profileSelector = (state) => state.profile | |||
| export const selectProfileName = createSelector( | |||
| profileSelector, | |||
| (state) => state?.company?.name | |||
| (state) => state?.mineProfile?.company?.name | |||
| ) | |||
| export const selectProfile = createSelector( | |||
| profileSelector, | |||
| (state) => state | |||
| (state) => state.profile | |||
| ) | |||
| export const selectMineProfile = createSelector( | |||
| profileSelector, | |||
| (state) => state?.mineProfile | |||
| ) | |||
| @@ -9,14 +9,18 @@ export const primaryThemeColors = { | |||
| primaryDarkGrayText: "#DCDCDC", | |||
| primaryIconBackgroundColor: "#E4E4E4", | |||
| borderNormal: "#D4D4D4", | |||
| primaryDarkTextSecond: "#E4E4E4", | |||
| borderSponsoredColor: "#E5D0FF", | |||
| backgroundSponsoredColor: "#F5EDFF", | |||
| offerBackgroundColor: "#F5F5F5", | |||
| selectOptionTextColor: "#1D1D1D", | |||
| primaryDarkText: "#505050", | |||
| primaryDarkTextThird: "#4D4D4D", | |||
| iconStrokeColor: "#8C8C8C", | |||
| // iconStrokeDisabledColor: "#818181" | |||
| iconStrokeDisabledColor: '#C4C4C4', | |||
| imagePickerBackground: "#E4E4E4", | |||
| iconYellowColor: "#FEB005" | |||
| iconYellowColor: "#FEB005", | |||
| iconMineProfileColor: "#9E9E9E", | |||
| iconProfileColor: "#C4C4C4" | |||
| } | |||