Преглед изворни кода

Merged without create-offer

feature/code-cleanup-joca
Djordje Mitrovic пре 3 година
родитељ
комит
da81195da6
45 измењених фајлова са 1320 додато и 173 уклоњено
  1. 5
    2
      src/AppRoutes.js
  2. 44
    0
      src/AppRoutes.js.orig
  3. 4
    0
      src/assets/images/svg/pocket.svg
  4. 5
    6
      src/components/Cards/ItemDetailsCard/ItemDetailsCard.js
  5. 63
    16
      src/components/Cards/ItemDetailsCard/ItemDetailsCard.styled.js
  6. 69
    60
      src/components/Cards/OfferCard/OfferCard.js
  7. 78
    7
      src/components/Cards/OfferCard/OfferCard.styled.js
  8. 19
    7
      src/components/Header/Header.js
  9. 3
    1
      src/components/Header/Header.styled.js
  10. 13
    8
      src/components/ItemDetails/ItemDetailsHeaderCard/ItemDetailsHeaderCard.js
  11. 73
    26
      src/components/ItemDetails/ItemDetailsHeaderCard/ItemDetailsHeaderCard.styled.js
  12. 2
    1
      src/components/Popovers/MyPosts/MyPosts.js
  13. 8
    4
      src/components/Popovers/MyProfile/MyProfile.js
  14. 20
    0
      src/components/Profile/Profile.js
  15. 6
    0
      src/components/Profile/Profile.styled.js
  16. 153
    0
      src/components/Profile/ProfileOffers/ProfileOffers.js
  17. 105
    0
      src/components/Profile/ProfileOffers/ProfileOffers.styled.js
  18. 202
    0
      src/components/ProfileCard/ProfileCard.js
  19. 222
    0
      src/components/ProfileCard/ProfileCard.styled.js
  20. 16
    5
      src/components/Scroller/HorizontalScroller.js
  21. 2
    2
      src/components/UserReviewsCard/UserReviewsCard.js
  22. 1
    0
      src/components/UserReviewsCard/UserReviewsCard.styled.js
  23. 1
    0
      src/constants/pages.js
  24. 1
    2
      src/hooks/useQueryString.js
  25. 2
    2
      src/layouts/ItemDetailsLayout/ItemDetailsLayout.js
  26. 8
    0
      src/layouts/ItemDetailsLayout/ItemDetailsLayout.styled.js
  27. 3
    0
      src/layouts/ProfileLayout/ProfileLayout.styled.js
  28. 43
    2
      src/pages/HomePage/HomePageMUI.js
  29. 30
    0
      src/pages/ProfilePage/ProfilePage.js
  30. 15
    0
      src/pages/ProfilePage/ProfilePage.styled.js
  31. 2
    2
      src/request/index.js
  32. 1
    1
      src/request/offersRequest.js
  33. 6
    0
      src/store/actions/offers/offersActionConstants.js
  34. 10
    0
      src/store/actions/offers/offersActions.js
  35. 5
    1
      src/store/actions/profile/profileActionConstants.js
  36. 8
    1
      src/store/actions/profile/profileActions.js
  37. 3
    1
      src/store/middleware/accessTokensMiddleware.js
  38. 1
    1
      src/store/reducers/filters/filtersReducer.js
  39. 10
    1
      src/store/reducers/offers/offersReducer.js
  40. 9
    1
      src/store/reducers/profile/profileReducer.js
  41. 15
    5
      src/store/saga/offersSaga.js
  42. 18
    4
      src/store/saga/profileSaga.js
  43. 4
    0
      src/store/selectors/offersSelectors.js
  44. 7
    3
      src/store/selectors/profileSelectors.js
  45. 5
    1
      src/themes/primaryTheme/primaryThemeColors.js

+ 5
- 2
src/AppRoutes.js Прегледај датотеку

import { import {
LOGIN_PAGE, LOGIN_PAGE,
HOME_PAGE, HOME_PAGE,
FORGOT_PASSWORD_PAGE,
NOT_FOUND_PAGE, NOT_FOUND_PAGE,
ERROR_PAGE, ERROR_PAGE,
BASE_PAGE, BASE_PAGE,
REGISTER_SUCCESSFUL_PAGE, REGISTER_SUCCESSFUL_PAGE,
RESET_PASSWORD_PAGE, RESET_PASSWORD_PAGE,
CREATE_OFFER_PAGE, CREATE_OFFER_PAGE,
ITEM_DETAILS_PAGE
ITEM_DETAILS_PAGE,
FORGOT_PASSWORD_PAGE,
PROFILE_PAGE
} from './constants/pages'; } from './constants/pages';
import LoginPage from './pages/LoginPage/LoginPage'; import LoginPage from './pages/LoginPage/LoginPage';
import HomePage from './pages/HomePage/HomePageMUI'; import HomePage from './pages/HomePage/HomePageMUI';
import ResetPasswordPage from './pages/ResetPasswordPage/ResetPasswordPage'; import ResetPasswordPage from './pages/ResetPasswordPage/ResetPasswordPage';
import CreateOffer from './pages/CreateOffer/CreateOffer'; import CreateOffer from './pages/CreateOffer/CreateOffer';
import ItemDetailsPage from './pages/ItemDetailsPage/ItemDetailsPageMUI'; import ItemDetailsPage from './pages/ItemDetailsPage/ItemDetailsPageMUI';
import ProfilePage from './pages/ProfilePage/ProfilePage';




const AppRoutes = () => { const AppRoutes = () => {
<Route path={RESET_PASSWORD_PAGE} component={ResetPasswordPage}/> <Route path={RESET_PASSWORD_PAGE} component={ResetPasswordPage}/>
<Route path={CREATE_OFFER_PAGE} component={CreateOffer}/> <Route path={CREATE_OFFER_PAGE} component={CreateOffer}/>
<Route path={ITEM_DETAILS_PAGE} component={ItemDetailsPage} /> <Route path={ITEM_DETAILS_PAGE} component={ItemDetailsPage} />
<Route path={PROFILE_PAGE} component={ProfilePage} />
<Route path={HOME_PAGE} component={HomePage} /> <Route path={HOME_PAGE} component={HomePage} />
{/* {/*
<PrivateRoute <PrivateRoute

+ 44
- 0
src/AppRoutes.js.orig Прегледај датотеку

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;

+ 4
- 0
src/assets/images/svg/pocket.svg Прегледај датотеку

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

+ 5
- 6
src/components/Cards/ItemDetailsCard/ItemDetailsCard.js Прегледај датотеку

Details, Details,
OfferDetails, OfferDetails,
OfferImage, OfferImage,
Scroller,
} from "./ItemDetailsCard.styled"; } from "./ItemDetailsCard.styled";
import { ReactComponent as Category } from "../../../assets/images/svg/category.svg"; import { ReactComponent as Category } from "../../../assets/images/svg/category.svg";
import { ReactComponent as Subcategory } from "../../../assets/images/svg/subcategory.svg"; import { ReactComponent as Subcategory } from "../../../assets/images/svg/subcategory.svg";
import { ReactComponent as Quantity } from "../../../assets/images/svg/quantity.svg"; import { ReactComponent as Quantity } from "../../../assets/images/svg/quantity.svg";
import { ReactComponent as Eye } from "../../../assets/images/svg/eye-striked.svg"; import { ReactComponent as Eye } from "../../../assets/images/svg/eye-striked.svg";
import selectedTheme from "../../../themes"; import selectedTheme from "../../../themes";
import HorizontalScroller from "../../Scroller/HorizontalScroller";
// import {ReactComponent as DummyImage1 } from "../../../assets/images/svg/dummyImages/offer-1.svg"




const ItemDetailsCard = (props) => { const ItemDetailsCard = (props) => {
<InfoText>{offer?.offer?.condition}</InfoText> <InfoText>{offer?.offer?.condition}</InfoText>
</InfoGroup> </InfoGroup>
<InfoGroup> <InfoGroup>
<InfoIcon color={"black"} component="span" size="12px">
<InfoIcon color={"black"} component="span" size="12px" last>
<Eye width={"18px"} height={"20px"} /> <Eye width={"18px"} height={"20px"} />
</InfoIcon> </InfoIcon>
<InfoText>{offer?.offer?.views?.viewers?.length}</InfoText> <InfoText>{offer?.offer?.views?.viewers?.length}</InfoText>
<PostDate>Objavljeno: {dayCreated}.{monthCreated}.{yearCreated}</PostDate> <PostDate>Objavljeno: {dayCreated}.{monthCreated}.{yearCreated}</PostDate>
</OfferInfo> </OfferInfo>
<Details> <Details>
<OfferTitle>{props.offer.name}</OfferTitle>
<HorizontalScroller>
<OfferTitle>{offer?.offer?.name}</OfferTitle>
<Scroller>
{offer?.offer?.images?.map(item => ( {offer?.offer?.images?.map(item => (
<OfferImage src={item} key={item} /> <OfferImage src={item} key={item} />
))} ))}
</HorizontalScroller>
</Scroller>
<OfferDetails> <OfferDetails>
<OfferDescriptionTitle>Opis:</OfferDescriptionTitle> <OfferDescriptionTitle>Opis:</OfferDescriptionTitle>
<OfferDescriptionText>{offer?.offer?.description}</OfferDescriptionText> <OfferDescriptionText>{offer?.offer?.description}</OfferDescriptionText>

+ 63
- 16
src/components/Cards/ItemDetailsCard/ItemDetailsCard.styled.js Прегледај датотеку

//import { IconButton } from "../../Buttons/IconButton/IconButton"; //import { IconButton } from "../../Buttons/IconButton/IconButton";
import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton"; import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton";
import { Icon } from "../../Icon/Icon"; import { Icon } from "../../Icon/Icon";
import HorizontalScroller from "../../Scroller/HorizontalScroller";


export const ItemDetailsCardContainer = styled(Container)` export const ItemDetailsCardContainer = styled(Container)`
display: flex; display: flex;
margin: 10px 0; margin: 10px 0;
border: 1px solid ${selectedTheme.borderNormal}; border: 1px solid ${selectedTheme.borderNormal};
background-color: ${(props) => background-color: ${(props) =>
props.sponsored === 'true' ? selectedTheme.backgroundSponsoredColor : "white"};
props.sponsored === "true"
? selectedTheme.backgroundSponsoredColor
: "white"};
border-radius: 4px; border-radius: 4px;
padding: 18px; padding: 18px;
max-width: 2000px; max-width: 2000px;
position: relative; position: relative;
@media (max-width: 600px) {
padding-bottom: 50px;
}
`; `;
export const OfferImage = styled.img` export const OfferImage = styled.img`
width: 144px; width: 144px;
flex: 2; flex: 2;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
margin:18px 0;
margin: 18px 0;
@media (max-width: 600px) {
margin: 0;
}
`; `;
export const InfoGroup = styled(Box)` 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)` 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)` 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)` export const InfoIcon = styled(Box)`
display:flex;
align-items:center;
display: flex;
align-items: center;
`; `;
export const InfoText = styled(Typography)` export const InfoText = styled(Typography)`
font-family: "Open Sans"; font-family: "Open Sans";
text-transform: capitalize; text-transform: capitalize;
@media (max-width: 600px) {
font-size: 12px;
}
`; `;
export const OfferTitle = styled(Typography)` export const OfferTitle = styled(Typography)`
font-family: "Open Sans"; font-family: "Open Sans";
font-weight: 700; font-weight: 700;
font-size: 24px; font-size: 24px;
padding: 0 72px; padding: 0 72px;
@media (max-width: 600px) {
padding: 0;
}
`; `;
export const OfferAuthor = styled(Box)` export const OfferAuthor = styled(Box)`
display: flex; display: flex;
flex-wrap: ${(props) => (!props.halfwidth ? "no-wrap" : "wrap")}; flex-wrap: ${(props) => (!props.halfwidth ? "no-wrap" : "wrap")};
justify-content: space-between; justify-content: space-between;
padding: 0 72px; padding: 0 72px;
@media (max-width: 600px) {
padding: 0;
}
`; `;
export const OfferCategory = styled(Box)` export const OfferCategory = styled(Box)`
font-family: "Open Sans"; font-family: "Open Sans";
font-size: 12px; font-size: 12px;
color: ${selectedTheme.primaryDarkText}; color: ${selectedTheme.primaryDarkText};
line-height: 16px; line-height: 16px;
@media (max-width: 600px) {
font-size: 9px;
line-height: 13px;
}

`; `;
export const OfferDescriptionText = styled(Box)` export const OfferDescriptionText = styled(Box)`
font-family: "Open Sans"; font-family: "Open Sans";
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 5; -webkit-line-clamp: 5;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
@media (max-width: 600px) {
font-size: 14px;
}
`; `;
export const OfferDescription = styled(Box)` export const OfferDescription = styled(Box)`
flex: 3; flex: 3;
background-color: ${selectedTheme.primaryPurple} !important; background-color: ${selectedTheme.primaryPurple} !important;
color: white !important; color: white !important;
} }
@media (max-width: 600px) {
height: 44px;
}
`; `;
export const Details = styled(Box)` 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 OfferImage = styled.img`
// ` // `
export const Scroller = styled(HorizontalScroller)`
& div {
margin: 0 9px;
}
`

+ 69
- 60
src/components/Cards/OfferCard/OfferCard.js Прегледај датотеку

const history = useHistory(); const history = useHistory();


const routeToItem = (itemId) => { const routeToItem = (itemId) => {
history.push(`/proizvodi/${itemId}`)
}
history.push(`/proizvodi/${itemId}`);
};
return ( return (
<React.Fragment> <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.title}
{props.description} {props.description}
{props.category} {props.category}
{props.quantity} {props.quantity}
{props.package} {props.package}
{props.numberOfViews} */} {props.numberOfViews} */}
</OfferFlexContainer>
</OfferCardContainer>
</OfferFlexContainer>
</OfferCardContainer>
</React.Fragment> </React.Fragment>
); );
}; };
halfwidth: PropTypes.bool, halfwidth: PropTypes.bool,
sponsored: PropTypes.bool, sponsored: PropTypes.bool,
offer: PropTypes.any, offer: PropTypes.any,
pinned: PropTypes.bool,
vertical: PropTypes.bool,
}; };
OfferCard.defaultProps = { OfferCard.defaultProps = {
halfwidth: false, halfwidth: false,

+ 78
- 7
src/components/Cards/OfferCard/OfferCard.styled.js Прегледај датотеку

height: 184px; height: 184px;
padding: 18px; padding: 18px;
padding-top: 12px; padding-top: 12px;
${(props) =>
props.vertical &&
`
height: 330px;
width: 180px;
margin: 0 18px;
`}
} }
`; `;
export const OfferFlexContainer = styled(Container)` export const OfferFlexContainer = styled(Container)`
margin: 0; margin: 0;
padding: 0; padding: 0;
max-height: 184px; max-height: 184px;
@media (max-width: 600px) {
${(props) =>
props.vertical &&
`
flex-direction: column;
`}
}
`; `;
export const OfferImage = styled.img` export const OfferImage = styled.img`
max-width: 144px; max-width: 144px;
width: 144px; width: 144px;
height: 144px; height: 144px;
@media (max-width: 600px) { @media (max-width: 600px) {
max-width: 108px;
${(props) =>
!props.vertical &&
`
max-width: 108px;
max-height: 108px; max-height: 108px;
width: 108px; width: 108px;
height: 108px; height: 108px;
`}
} }
`; `;
export const OfferInfo = styled(Box)` export const OfferInfo = styled(Box)`
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
margin-left: 18px; margin-left: 18px;
${(props) =>
props.vertical &&
`
margin-left: 0;
margin-top: 5px;
`}
`; `;
export const OfferTitle = styled(Typography)` export const OfferTitle = styled(Typography)`
font-family: "Open Sans"; font-family: "Open Sans";
color: ${selectedTheme.primaryPurple}; color: ${selectedTheme.primaryPurple};
font-weight: 700; font-weight: 700;
font-size: 24px; font-size: 24px;
cursor: pointer;
@media (max-width: 550px) { @media (max-width: 550px) {
font-size: 18px; font-size: 18px;
display: none; 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)` export const OfferAuthor = styled(Box)`
color: ${selectedTheme.primaryText}; color: ${selectedTheme.primaryText};
@media (max-width: 600px) { @media (max-width: 600px) {
font-size: 14px; 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)` export const OfferLocation = styled(Typography)`
color: ${selectedTheme.primaryDarkText}; color: ${selectedTheme.primaryDarkText};
line-height: 16px; line-height: 16px;
font-size: 12px; font-size: 12px;
${(props) =>
props.vertical &&
`
font-size: 12px;
margin-top: 5px;
position: absolute;
bottom: 61px;
`}
`; `;
export const OfferDetails = styled(Box)` export const OfferDetails = styled(Box)`
display: flex; display: flex;
color: ${selectedTheme.primaryText}; color: ${selectedTheme.primaryText};
line-height: 16px; line-height: 16px;
font-size: 12px; font-size: 12px;
${(props) =>
props.vertical &&
`
position: absolute;
bottom: 15px;
`}
`; `;
export const OfferPackage = styled(Box)` export const OfferPackage = styled(Box)`
font-family: "Open Sans"; font-family: "Open Sans";
color: ${selectedTheme.primaryText}; color: ${selectedTheme.primaryText};
line-height: 16px; line-height: 16px;
font-size: 12px; font-size: 12px;
${(props) =>
props.vertical &&
`
display: none;
`}
`; `;
export const OfferDescriptionTitle = styled(Box)` export const OfferDescriptionTitle = styled(Box)`
font-family: "Open Sans"; font-family: "Open Sans";
top: 16px; top: 16px;
right: 16px; right: 16px;
padding: 0; padding: 0;
${(props) =>
props.vertical &&
`
display: none;
`}
& button svg { & button svg {
width: 16px; width: 16px;
height: 16px; height: 16px;
width: 144px; width: 144px;
height: 144px; height: 144px;
@media (max-width: 600px) { @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; border-radius: 4px;
overflow: hidden; overflow: hidden;
box-shadow: 4px 4px 9px rgba(0, 0, 0, 0.12); box-shadow: 4px 4px 9px rgba(0, 0, 0, 0.12);
padding-top: 5px; padding-top: 5px;
padding-left: 1px; padding-left: 1px;
display: block; display: block;
${(props) => props.vertical && `display: none;`}
@media (min-width: 551px) { @media (min-width: 551px) {
display: none; display: none;
} }

+ 19
- 7
src/components/Header/Header.js Прегледај датотеку

import { selectUserId } from "../../store/selectors/loginSelectors"; import { selectUserId } from "../../store/selectors/loginSelectors";
import { useSearch } from "../../hooks/useSearch"; import { useSearch } from "../../hooks/useSearch";
import { selectProfileName } from "../../store/selectors/profileSelectors"; import { selectProfileName } from "../../store/selectors/profileSelectors";
import { fetchProfile } from "../../store/actions/profile/profileActions";
import { useHistory, useRouteMatch } from "react-router-dom"; 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 useFilters from "../../hooks/useFilters";
import FilterCard from "../Cards/FilterCard/FilterCard"; import FilterCard from "../Cards/FilterCard/FilterCard";
import { useQueryString } from "../../hooks/useQueryString"; import { useQueryString } from "../../hooks/useQueryString";
import { convertQueryStringFrontend } from "../../util/helpers/queryHelpers"; import { convertQueryStringFrontend } from "../../util/helpers/queryHelpers";
import { fetchMineProfile } from "../../store/actions/profile/profileActions";


const Header = () => { const Header = () => {
const [openDrawer, setOpenDrawer] = useState(false); const [openDrawer, setOpenDrawer] = useState(false);
const [openFilters, setOpenFilters] = useState(false); const [openFilters, setOpenFilters] = useState(false);
const [showSearchBar, setShowSearchBar] = useState(true);
const [numberOfFilters, setNumberOfFilters] = useState(0); const [numberOfFilters, setNumberOfFilters] = useState(0);
const { t } = useTranslation(); const { t } = useTranslation();
const theme = useTheme(); const theme = useTheme();
const searchMobileRef = useRef(null); const searchMobileRef = useRef(null);
const queryStringHook = useQueryString(); const queryStringHook = useQueryString();
useEffect(() => { useEffect(() => {
if (user?.length > 1) {
dispatch(fetchProfile(user));
}
}, [user]);
dispatch(fetchMineProfile());
}, []);
useEffect(() => { useEffect(() => {
setUserPopoverOpen(false); setUserPopoverOpen(false);
setUserAnchorEl(null); setUserAnchorEl(null);
setUserAnchorEl(null); setUserAnchorEl(null);
}; };
}, []); }, []);
useEffect(() => {
if (history.location.pathname !== "/home") {
setShowSearchBar(false);
} else {
setShowSearchBar(true);
}
}, [history.location.pathname])
useEffect(() => { useEffect(() => {
setNumberOfFilters(filters.calculateFiltersChosen()); setNumberOfFilters(filters.calculateFiltersChosen());
}, [ }, [
if ( if (
location.pathname === "/login" || location.pathname === "/login" ||
location.pathname === "/register" || location.pathname === "/register" ||
location.pathname === "/register/success" ||
location.pathname === "/forgot-password" || location.pathname === "/forgot-password" ||
location.pathname === "/reset-password" || location.pathname === "/reset-password" ||
location.pathname === "/" location.pathname === "/"
setOpenFilters((prevState) => !prevState); setOpenFilters((prevState) => !prevState);
}; };


const handleLogoClick = () => {
history.push(HOME_PAGE);
}

return ( return (
<HeaderContainer style={{ display: shouldShow ? "block" : "none" }}> <HeaderContainer style={{ display: shouldShow ? "block" : "none" }}>
<AppBar <AppBar
> >
<Toolbar> <Toolbar>
<ToolsContainer> <ToolsContainer>
<LogoContainer>
<LogoContainer onClick={() => handleLogoClick()}>
<LogoHorizontal /> <LogoHorizontal />
</LogoContainer> </LogoContainer>
{matches && ( {matches && (
</AppBar> </AppBar>
<SearchInputMobile <SearchInputMobile
fullWidth fullWidth
shouldShow={showSearchBar}
ref={searchMobileRef} ref={searchMobileRef}
InputProps={{ InputProps={{
endAdornment: ( endAdornment: (

+ 3
- 1
src/components/Header/Header.styled.js Прегледај датотеку

display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
cursor: pointer;
`; `;
export const ToolsButtonsContainer = styled(Box)` export const ToolsButtonsContainer = styled(Box)`
display: flex; display: flex;
width: 80%; width: 80%;
top: 70px; top: 70px;
height: 46px; height: 46px;
left: -50px;
left: -5px;
font-family: "Open Sans"; font-family: "Open Sans";
${props => !props.shouldShow && `display: none;`}
& div { & div {
background-color: white; background-color: white;
height: 40px; height: 40px;

+ 13
- 8
src/components/ItemDetails/ItemDetailsHeaderCard/ItemDetailsHeaderCard.js Прегледај датотеку

HeaderDetails, HeaderDetails,
BottomDetails, BottomDetails,
StatusText, StatusText,
PIBIcon,
} from "./ItemDetailsHeaderCard.styled"; } from "./ItemDetailsHeaderCard.styled";
import { ItemDetailsHeaderContainer } from "./ItemDetailsHeaderCard.styled"; import { ItemDetailsHeaderContainer } from "./ItemDetailsHeaderCard.styled";
import { ReactComponent as Category } from "../../../assets/images/svg/category.svg"; import { ReactComponent as Category } from "../../../assets/images/svg/category.svg";
import { ReactComponent as PIB } from "../../../assets/images/svg/pib.svg"; import { ReactComponent as PIB } from "../../../assets/images/svg/pib.svg";
import { ReactComponent as MessageColor } from "../../../assets/images/svg/mailColor.svg"; import { ReactComponent as MessageColor } from "../../../assets/images/svg/mailColor.svg";
import selectedTheme from "../../../themes"; import selectedTheme from "../../../themes";
import { useHistory } from "react-router-dom";


const ItemDetailsHeaderCard = (props) => { const ItemDetailsHeaderCard = (props) => {
const history = useHistory();
console.log("header offer: ", props.offer); console.log("header offer: ", props.offer);
const offer = props.offer; const offer = props.offer;
if (!props.offer) { if (!props.offer) {
100 100
); );
} }
const handleGoProfile = () => {
history.push(`/profile/${offer?.offer?.userId}`);
}
return ( return (
<ItemDetailsHeaderContainer <ItemDetailsHeaderContainer
sponsored={props.sponsored.toString()}
sponsored={offer?.offer?.pinned?.toString()}
halfwidth={props.halfwidth ? 1 : 0} halfwidth={props.halfwidth ? 1 : 0}
> >
<HeaderTop> <HeaderTop>
<OfferImage src={offer?.companyData?.image}/> <OfferImage src={offer?.companyData?.image}/>
<OfferDetails> <OfferDetails>
<OfferTitle>{offer?.companyData?.company?.name}</OfferTitle>
<OfferTitle onClick={handleGoProfile}>{offer?.companyData?.company?.name}</OfferTitle>
<DetailContainer> <DetailContainer>
<DetailIcon
<PIBIcon
color={selectedTheme.iconStrokeColor} color={selectedTheme.iconStrokeColor}
component="span" component="span"
size="22px"
> >
<PIB width={"22px"} />
</DetailIcon>
<PIB />
</PIBIcon>
<DetailText>PIB - {offer?.companyData?.company?.PIB}</DetailText> <DetailText>PIB - {offer?.companyData?.company?.PIB}</DetailText>
</DetailContainer> </DetailContainer>
<DetailContainer>
<DetailContainer shouldHideResponsive>
<DetailIcon <DetailIcon
color={selectedTheme.iconStrokeColor} color={selectedTheme.iconStrokeColor}
component="span" component="span"
> >
<Category width={"22px"} /> <Category width={"22px"} />
</DetailIcon> </DetailIcon>
<DetailText>
<DetailText >
{offer?.companyData?.company?.contacts?.location} {offer?.companyData?.company?.contacts?.location}
</DetailText> </DetailText>
</DetailContainer> </DetailContainer>

+ 73
- 26
src/components/ItemDetails/ItemDetailsHeaderCard/ItemDetailsHeaderCard.styled.js Прегледај датотеку

box-sizing: border-box; box-sizing: border-box;
margin: 10px 0; margin: 10px 0;
background-color: ${(props) => background-color: ${(props) =>
props.sponsored === 'true' ? selectedTheme.backgroundSponsoredColor : "white"};
props.sponsored === "true"
? selectedTheme.backgroundSponsoredColor
: "white"};
border-radius: 4px; border-radius: 4px;
border: 1px solid ${selectedTheme.borderNormal}; border: 1px solid ${selectedTheme.borderNormal};
max-width: 2000px; max-width: 2000px;
position: relative; position: relative;
`; `;
export const DetailContainer = styled(Box)` 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)` 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)` export const HeaderDetails = styled(Box)`
background-color: ${selectedTheme.primaryIconBackgroundColor};
`;
background-color: ${selectedTheme.primaryIconBackgroundColor};
`;
export const BottomDetails = styled(Box)` export const BottomDetails = styled(Box)`
max-width:fit-content;
max-width: fit-content;
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 1fr); grid-template-rows: repeat(2, 1fr);
grid-column-gap: 12px; grid-column-gap: 12px;
grid-row-gap: 12px;
grid-row-gap: 12px;
padding: 18px; padding: 18px;
@media (max-width: 600px) {
display: flex;
flex-direction: column;
}
`; `;
export const OfferImage = styled.img` 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)` export const OfferInfo = styled(Box)`
display: flex; display: flex;
margin-left: 18px; margin-left: 18px;
`; `;
export const OfferTitle = styled(Typography)` export const OfferTitle = styled(Typography)`
margin-bottom:12px;
margin-bottom: 12px;
font-family: "Open Sans"; font-family: "Open Sans";
color: ${selectedTheme.primaryPurple}; color: ${selectedTheme.primaryPurple};
font-weight: 700; font-weight: 700;
font-size: 24px; font-size: 24px;
cursor: pointer;
@media (max-width: 600px) {
font-size: 18px;
}
`; `;
export const OfferAuthor = styled(Box)` export const OfferAuthor = styled(Box)`
display: flex; display: flex;
`; `;


export const StatusText = styled(Grid)` 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)` export const OfferCategory = styled(Box)`
font-family: "Open Sans"; font-family: "Open Sans";
color: ${selectedTheme.primaryText}; color: ${selectedTheme.primaryText};
margin: auto 0; margin: auto 0;
`; `;
export const DetailIcon = styled(Icon)` export const DetailIcon = styled(Icon)`
display:flex;
align-items:center;
display: flex;
align-items: center;
& svg { & svg {
width: 22px; width: 22px;
position: relative; position: relative;
line-height: 16px; line-height: 16px;
font-size: 16px; font-size: 16px;
position: relative; position: relative;
@media (max-width: 600px) {
font-size: 14px;
}
`; `;
export const CheckButton = styled(PrimaryButton)` export const CheckButton = styled(PrimaryButton)`
width: 180px; width: 180px;
border-radius: 100%; border-radius: 100%;
padding-top: 2px; padding-top: 2px;
text-align: center; 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;
}
}
`; `;

+ 2
- 1
src/components/Popovers/MyPosts/MyPosts.js Прегледај датотеку

useEffect(() => { useEffect(() => {
dispatch(fetchMineOffers()); dispatch(fetchMineOffers());
}, []); }, []);
console.log("mineOffers: ", mineOffers)
useEffect(() => { useEffect(() => {
if (mineOffers?.length > 0) { if (mineOffers?.length > 0) {
if (mineOffers.length > 1) { if (mineOffers.length > 1) {
setArrayOfMineOffers([]) setArrayOfMineOffers([])
} }
} }
});
}, [mineOffers]);
return ( return (
<HeaderPopover <HeaderPopover
title={t("header.myOffers")} title={t("header.myOffers")}

+ 8
- 4
src/components/Popovers/MyProfile/MyProfile.js Прегледај датотеку

import HeaderPopover from "../HeaderPopover/HeaderPopover"; import HeaderPopover from "../HeaderPopover/HeaderPopover";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux"; 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 { selectUserId } from "../../../store/selectors/loginSelectors";
import { fetchProfile } from "../../../store/actions/profile/profileActions";
import { fetchMineProfile } from "../../../store/actions/profile/profileActions";
import selectedTheme from "../../../themes"; import selectedTheme from "../../../themes";
import { EyeIcon } from "../HeaderPopover/HeaderPopover.styled"; import { EyeIcon } from "../HeaderPopover/HeaderPopover.styled";
import { logoutUser } from "../../../store/actions/login/loginActions"; import { logoutUser } from "../../../store/actions/login/loginActions";


export const MyProfile = () => { export const MyProfile = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const profile = useSelector(selectProfile);
const profile = useSelector(selectMineProfile);
const userId = useSelector(selectUserId); const userId = useSelector(selectUserId);
const dispatch = useDispatch(); const dispatch = useDispatch();
const history = useHistory(); const history = useHistory();
const [profileAsArray, setProfileAsArray] = useState([]); const [profileAsArray, setProfileAsArray] = useState([]);
useEffect(() => { useEffect(() => {
if (userId?.length > 1) { if (userId?.length > 1) {
dispatch(fetchProfile(userId));
dispatch(fetchMineProfile());
} }
}, [userId]); }, [userId]);
useEffect(() => { useEffect(() => {
const handleLogout = () => { const handleLogout = () => {
dispatch(logoutUser(handleLogoutSuccess)); dispatch(logoutUser(handleLogoutSuccess));
}; };
const seeMyProfile = () => {
history.push(`/profile/${userId}`);
}
return ( return (
<HeaderPopover <HeaderPopover
title={t("header.myProfile")} title={t("header.myProfile")}
buttonText={t("header.checkProfile")} buttonText={t("header.checkProfile")}
buttonIcon={<EyeIcon color={selectedTheme.iconYellowColor} />} buttonIcon={<EyeIcon color={selectedTheme.iconYellowColor} />}
isProfile isProfile
buttonOnClick={() => seeMyProfile()}
secondButtonIcon={<LogoutIcon color={selectedTheme.iconYellowColor} />} secondButtonIcon={<LogoutIcon color={selectedTheme.iconYellowColor} />}
secondButtonText={"Odjavite se"} secondButtonText={"Odjavite se"}
secondButtonOnClick={handleLogout} secondButtonOnClick={handleLogout}

+ 20
- 0
src/components/Profile/Profile.js Прегледај датотеку

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

+ 6
- 0
src/components/Profile/Profile.styled.js Прегледај датотеку

import { Box } from "@mui/material";
import styled from "styled-components";

export const ProfileContainer = styled(Box)`

`

+ 153
- 0
src/components/Profile/ProfileOffers/ProfileOffers.js Прегледај датотеку

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;

+ 105
- 0
src/components/Profile/ProfileOffers/ProfileOffers.styled.js Прегледај датотеку

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

+ 202
- 0
src/components/ProfileCard/ProfileCard.js Прегледај датотеку

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;

+ 222
- 0
src/components/ProfileCard/ProfileCard.styled.js Прегледај датотеку

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

+ 16
- 5
src/components/Scroller/HorizontalScroller.js Прегледај датотеку

scrollRef.current.scrollBy({ left: -50, behaviour: "smooth" }); scrollRef.current.scrollBy({ left: -50, behaviour: "smooth" });
}; };
return ( 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 <ListContainer
innerRef={scrollRef} innerRef={scrollRef}
style={props.listStyle} style={props.listStyle}
> >
{props.children} {props.children}
</ListContainer> </ListContainer>
<ArrowButton onClick={handleRight} disabled={isDisabledRightButton} side={"right"}>
</ArrowButton>
{!props.hideArrows && (
<ArrowButton
onClick={handleRight}
disabled={isDisabledRightButton}
side={"right"}
></ArrowButton>
)}
</HorizontalScrollerContainer> </HorizontalScrollerContainer>
); );
}; };
className: PropTypes.string, className: PropTypes.string,
containerStyle: PropTypes.any, containerStyle: PropTypes.any,
listStyle: PropTypes.any, listStyle: PropTypes.any,
hideArrows: PropTypes.bool,
}; };


export default HorizontalScroller; export default HorizontalScroller;

+ 2
- 2
src/components/UserReviewsCard/UserReviewsCard.js Прегледај датотеку

<Typography>{props.heading}</Typography> <Typography>{props.heading}</Typography>
</Grid> </Grid>
<ReviewList> <ReviewList>
{dataMockupdata.map((review) => (
{offer?.companyData?.lastThreeReviews?.map((review) => (
<> <>
<ListItem <ListItem
alignItems="flex-start" alignItems="flex-start"
variant="body2" variant="body2"
color="text.primary" color="text.primary"
> >
&quot;{review.quote}&quot;
&quot;{review?.quote}&quot;
</Typography> </Typography>
</Grid> </Grid>
</Grid> </Grid>

+ 1
- 0
src/components/UserReviewsCard/UserReviewsCard.styled.js Прегледај датотеку

padding: 2rem; padding: 2rem;
border-radius: 4px 0 0 4px; border-radius: 4px 0 0 4px;
height: 100%; height: 100%;
width: 100%;
overflow-y: auto; overflow-y: auto;
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 5px; width: 5px;

+ 1
- 0
src/constants/pages.js Прегледај датотеку

export const RESET_PASSWORD_PAGE = "/reset-password/:token"; export const RESET_PASSWORD_PAGE = "/reset-password/:token";
export const CREATE_OFFER_PAGE = "/create-offer"; export const CREATE_OFFER_PAGE = "/create-offer";
export const ITEM_DETAILS_PAGE = "/proizvodi/:idProizvod"; export const ITEM_DETAILS_PAGE = "/proizvodi/:idProizvod";
export const PROFILE_PAGE = "/profile/:idProfile"

+ 1
- 2
src/hooks/useQueryString.js Прегледај датотеку

// import _ from "lodash" // import _ from "lodash"
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { HOME_PAGE } from "../constants/pages";
import { setQueryString as setQueryStringSaga } from "../store/actions/queryString/queryStringActions"; import { setQueryString as setQueryStringSaga } from "../store/actions/queryString/queryStringActions";
import { selectQueryString } from "../store/selectors/queryStringSelectors"; import { selectQueryString } from "../store/selectors/queryStringSelectors";
// import useFilters from "./useFilters"; // import useFilters from "./useFilters";
useEffect(() => { useEffect(() => {
if (!initial) { if (!initial) {
history.push({ history.push({
pathname: HOME_PAGE,
pathname: history.location.pathname,
search: "?" + globalQueryString, search: "?" + globalQueryString,
}); });
} }

+ 2
- 2
src/layouts/ItemDetailsLayout/ItemDetailsLayout.js Прегледај датотеку

<ItemDetailsLayoutContainer> <ItemDetailsLayoutContainer>
{props.children} {props.children}
<Grid container maxHeight="xl" spacing={2}> <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} {props.content}
</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} {props.rightCard}
</RightCard> </RightCard>

+ 8
- 0
src/layouts/ItemDetailsLayout/ItemDetailsLayout.styled.js Прегледај датотеку

display: flex; display: flex;
flex: 1; flex: 1;
height: 100%; height: 100%;
@media (max-width: 1024px) {
padding-right: 54px;
}
@media (max-width: 600px) {
padding-left: 18px;
padding-right: 18px;
}
` `


export const RightCard = styled(Grid)` export const RightCard = styled(Grid)`
margin-top: 30px; margin-top: 30px;
border-top-right-radius: 4px; border-top-right-radius: 4px;
width: 100%;
` `
export const Content = styled(Grid)` export const Content = styled(Grid)`
` `

+ 3
- 0
src/layouts/ProfileLayout/ProfileLayout.styled.js Прегледај датотеку

flex: 1; flex: 1;
height: 100%; height: 100%;
margin-top: 80px; margin-top: 80px;
@media (max-width: 600px) {
margin-top: 40px;
}
` `


export const LeftCard = styled(Grid)` export const LeftCard = styled(Grid)`

+ 43
- 2
src/pages/HomePage/HomePageMUI.js Прегледај датотеку

<MainLayout leftCard={<FilterCard />} content={<MarketPlace />} /> <MainLayout leftCard={<FilterCard />} content={<MarketPlace />} />
</HomePageContainer> </HomePageContainer>
); );
};

}
export default HomePage; 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>
// </>
// );
// };


+ 30
- 0
src/pages/ProfilePage/ProfilePage.js Прегледај датотеку

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;

+ 15
- 0
src/pages/ProfilePage/ProfilePage.styled.js Прегледај датотеку

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
- 2
src/request/index.js Прегледај датотеку

import queryString from "qs"; import queryString from "qs";


const request = axios.create({ 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: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },

+ 1
- 1
src/request/offersRequest.js Прегледај датотеку

export const attemptAddOffer = (payload) => { export const attemptAddOffer = (payload) => {
return postRequest(apiEndpoints.offers.addOffer, payload) return postRequest(apiEndpoints.offers.addOffer, payload)
} }
export const attemptFetchMineOffers = (payload) => {
export const attemptFetchProfileOffers = (payload) => {
return getRequest(`${apiEndpoints.offers.mineOffers}/${payload}/offers`) return getRequest(`${apiEndpoints.offers.mineOffers}/${payload}/offers`)
} }

+ 6
- 0
src/store/actions/offers/offersActionConstants.js Прегледај датотеку

export const OFFERS_ERROR = createErrorType(OFFERS_SCOPE); export const OFFERS_ERROR = createErrorType(OFFERS_SCOPE);
export const OFFERS_CLEAR = createClearType(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_FETCH = createFetchType(ONE_OFFER_SCOPE);
export const ONE_OFFER_SUCCESS = createSuccessType(ONE_OFFER_FETCH); export const ONE_OFFER_SUCCESS = createSuccessType(ONE_OFFER_FETCH);
export const ONE_OFFER_ERROR = createErrorType(ONE_OFFER_SCOPE); export const ONE_OFFER_ERROR = createErrorType(ONE_OFFER_SCOPE);
export const OFFERS_SET_TOTAL = "OFFERS_SET_TOTAL"; export const OFFERS_SET_TOTAL = "OFFERS_SET_TOTAL";
export const OFFERS_MINE_SET = "OFFERS_MY_ADD"; export const OFFERS_MINE_SET = "OFFERS_MY_ADD";
export const OFFER_ADD = "OFFER_ADD"; export const OFFER_ADD = "OFFER_ADD";
export const OFFERS_PROFILE_SET = "OFFERS_PROFILE_SET";

+ 10
- 0
src/store/actions/offers/offersActions.js Прегледај датотеку

OFFERS_NO_MORE, OFFERS_NO_MORE,
OFFERS_PINNED_ADD, OFFERS_PINNED_ADD,
OFFERS_PINNED_SET, OFFERS_PINNED_SET,
OFFERS_PROFILE_FETCH,
OFFERS_PROFILE_SET,
OFFERS_SET, OFFERS_SET,
OFFERS_SET_TOTAL, OFFERS_SET_TOTAL,
OFFERS_SUCCESS, OFFERS_SUCCESS,
type: OFFERS_MINE_SET, type: OFFERS_MINE_SET,
payload, payload,
}); });
export const fetchProfileOffers = (payload) => ({
type: OFFERS_PROFILE_FETCH,
payload,
})
export const setProfileOffers = (payload) => ({
type: OFFERS_PROFILE_SET,
payload,
})

+ 5
- 1
src/store/actions/profile/profileActionConstants.js Прегледај датотеку

export const PROFILE_SUCCESS = createSuccessType(PROFILE_SCOPE); export const PROFILE_SUCCESS = createSuccessType(PROFILE_SCOPE);
export const PROFILE_ERROR = createErrorType(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";

+ 8
- 1
src/store/actions/profile/profileActions.js Прегледај датотеку

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) => ({ export const fetchProfile = (payload) => ({
type: PROFILE_FETCH, type: PROFILE_FETCH,
export const setProfile = (payload) => ({ export const setProfile = (payload) => ({
type: PROFILE_SET, type: PROFILE_SET,
payload, payload,
})
export const setMineProfile = (payload) => ({
type: PROFILE_MINE_SET,
payload,
})
export const fetchMineProfile = () => ({
type: PROFILE_MINE_FETCH,
}) })

+ 3
- 1
src/store/middleware/accessTokensMiddleware.js Прегледај датотеку

// import { setUserAccessToken } from "../actions/user/userActions"; // import { setUserAccessToken } from "../actions/user/userActions";


//Change URL with .env //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/"; // const baseURL = "http://192.168.88.175:3005/";


//Interceptor unique name //Interceptor unique name
const jwtToken = authScopeStringGetHelper(JWT_TOKEN); const jwtToken = authScopeStringGetHelper(JWT_TOKEN);
const refresh = authScopeStringGetHelper(JWT_REFRESH_TOKEN); const refresh = authScopeStringGetHelper(JWT_REFRESH_TOKEN);
if (!jwtToken || !refresh) return Promise.resolve(response); if (!jwtToken || !refresh) return Promise.resolve(response);
console.log('ispod je');
const jwtTokenDecoded = jwt.decode(jwtToken); const jwtTokenDecoded = jwt.decode(jwtToken);
const refreshTokenDecoded = jwt.decode(refresh); const refreshTokenDecoded = jwt.decode(refresh);
if (!response.headers?.Authorization) { if (!response.headers?.Authorization) {
} }
// If access token is expired, refresh access token // If access token is expired, refresh access token
if (new Date() > new Date(jwtTokenDecoded.exp * 1000)) { if (new Date() > new Date(jwtTokenDecoded.exp * 1000)) {
console.log('untura je')
const axiosResponse = await axios.post(`${baseURL}auth/refresh`, { const axiosResponse = await axios.post(`${baseURL}auth/refresh`, {
token: refresh, token: refresh,
}); });

+ 1
- 1
src/store/reducers/filters/filtersReducer.js Прегледај датотеку

isApplied: payload, isApplied: payload,
} }
} }
}
}

+ 10
- 1
src/store/reducers/offers/offersReducer.js Прегледај датотеку

ONE_OFFER_ERROR, ONE_OFFER_ERROR,
ONE_OFFER_SUCCESS, ONE_OFFER_SUCCESS,
OFFERS_SET_TOTAL, OFFERS_SET_TOTAL,
OFFERS_PROFILE_SET,
} from "../../actions/offers/offersActionConstants"; } from "../../actions/offers/offersActionConstants";
import createReducer from "../../utils/createReducer"; import createReducer from "../../utils/createReducer";


offers: [], offers: [],
pinnedOffers: [], pinnedOffers: [],
mineOffers: [], mineOffers: [],
profileOffers: [],
total: 0, total: 0,
error: "", error: "",
newOffer: "", newOffer: "",
[OFFERS_PINNED_ADD]: addPinnedOffers, [OFFERS_PINNED_ADD]: addPinnedOffers,
[OFFERS_PINNED_SET]: setPinnedOffers, [OFFERS_PINNED_SET]: setPinnedOffers,
[OFFERS_SET_TOTAL]: setTotalOffers, [OFFERS_SET_TOTAL]: setTotalOffers,
[OFFERS_MINE_SET]: setMineOffers
[OFFERS_MINE_SET]: setMineOffers,
[OFFERS_PROFILE_SET]: setProfileOffers,
}, },
initialState initialState
); );
mineOffers: action.payload mineOffers: action.payload
} }
} }
function setProfileOffers(state, action) {
return {
...state,
profileOffers: action.payload
}
}

+ 9
- 1
src/store/reducers/profile/profileReducer.js Прегледај датотеку

import { PROFILE_SET } from "../../actions/profile/profileActionConstants";
import { PROFILE_MINE_SET, PROFILE_SET } from "../../actions/profile/profileActionConstants";
import createReducer from "../../utils/createReducer"; import createReducer from "../../utils/createReducer";


const initialState = { const initialState = {
profile: {}, profile: {},
mineProfile: {},
}; };


export default createReducer( export default createReducer(
{ {
[PROFILE_SET]: setProfile, [PROFILE_SET]: setProfile,
[PROFILE_MINE_SET]: setMineProfile,
}, },
initialState initialState
); );
profile: action.payload, profile: action.payload,
}; };
} }
function setMineProfile(state, action) {
return {
...state,
mineProfile: action.payload
}
}

+ 15
- 5
src/store/saga/offersSaga.js Прегледај датотеку

import { attemptAddOffer, attemptFetchOffers, attemptFetchOneOffer } from "../../request/offersRequest"; 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 { all, takeLatest, call, put, select } from "@redux-saga/core/effects";
import { import {
attemptFetchMineOffers,
attemptFetchProfileOffers,
attemptFetchMoreOffers, attemptFetchMoreOffers,
} from "../../request/offersRequest"; } from "../../request/offersRequest";
import { convertQueryStringBackend } from "../../util/helpers/queryHelpers"; import { convertQueryStringBackend } from "../../util/helpers/queryHelpers";
} }
} }



function* createOffer(payload) { function* createOffer(payload) {
try { try {
const data = yield call(attemptAddOffer, payload); const data = yield call(attemptAddOffer, payload);
function* fetchMineOffers() { function* fetchMineOffers() {
try { try {
const userId = yield select(selectUserId); const userId = yield select(selectUserId);
const data = yield call(attemptFetchMineOffers, userId);
const data = yield call(attemptFetchProfileOffers, userId);
yield put(setMineOffers(data.data)); yield put(setMineOffers(data.data));
} catch (e) { } catch (e) {
console.log(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() { export default function* offersSaga() {
yield all( yield all(
[ [
takeLatest(ONE_OFFER_FETCH, fetchOneOffer), takeLatest(ONE_OFFER_FETCH, fetchOneOffer),
takeLatest(OFFERS_FETCH_MORE, fetchMoreOffers), takeLatest(OFFERS_FETCH_MORE, fetchMoreOffers),
takeLatest(OFFERS_MINE_FETCH, fetchMineOffers), takeLatest(OFFERS_MINE_FETCH, fetchMineOffers),
takeLatest(OFFERS_PROFILE_FETCH, fetchProfileOffers)
]); ]);
} }

+ 18
- 4
src/store/saga/profileSaga.js Прегледај датотеку

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 { 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) { function* fetchProfile(payload) {
try { try {
} }
} }


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() { export default function* profileSaga() {
yield all([ yield all([
takeLatest(PROFILE_FETCH, fetchProfile)
takeLatest(PROFILE_FETCH, fetchProfile),
takeLatest(PROFILE_MINE_FETCH, fetchMineProfile)
]) ])
} }

+ 4
- 0
src/store/selectors/offersSelectors.js Прегледај датотеку

offersSelector, offersSelector,
(state) => state.mineOffers (state) => state.mineOffers
) )
export const selectProfileOffers = createSelector(
offersSelector,
(state) => state.profileOffers
)

+ 7
- 3
src/store/selectors/profileSelectors.js Прегледај датотеку

import { createSelector } from "reselect"; import { createSelector } from "reselect";


const profileSelector = (state) => state.profile.profile;
const profileSelector = (state) => state.profile


export const selectProfileName = createSelector( export const selectProfileName = createSelector(
profileSelector, profileSelector,
(state) => state?.company?.name
(state) => state?.mineProfile?.company?.name
) )
export const selectProfile = createSelector( export const selectProfile = createSelector(
profileSelector, profileSelector,
(state) => state
(state) => state.profile
)
export const selectMineProfile = createSelector(
profileSelector,
(state) => state?.mineProfile
) )

+ 5
- 1
src/themes/primaryTheme/primaryThemeColors.js Прегледај датотеку

primaryDarkGrayText: "#DCDCDC", primaryDarkGrayText: "#DCDCDC",
primaryIconBackgroundColor: "#E4E4E4", primaryIconBackgroundColor: "#E4E4E4",
borderNormal: "#D4D4D4", borderNormal: "#D4D4D4",
primaryDarkTextSecond: "#E4E4E4",
borderSponsoredColor: "#E5D0FF", borderSponsoredColor: "#E5D0FF",
backgroundSponsoredColor: "#F5EDFF", backgroundSponsoredColor: "#F5EDFF",
offerBackgroundColor: "#F5F5F5", offerBackgroundColor: "#F5F5F5",
selectOptionTextColor: "#1D1D1D", selectOptionTextColor: "#1D1D1D",
primaryDarkText: "#505050", primaryDarkText: "#505050",
primaryDarkTextThird: "#4D4D4D",
iconStrokeColor: "#8C8C8C", iconStrokeColor: "#8C8C8C",
// iconStrokeDisabledColor: "#818181" // iconStrokeDisabledColor: "#818181"
iconStrokeDisabledColor: '#C4C4C4', iconStrokeDisabledColor: '#C4C4C4',
imagePickerBackground: "#E4E4E4", imagePickerBackground: "#E4E4E4",
iconYellowColor: "#FEB005"
iconYellowColor: "#FEB005",
iconMineProfileColor: "#9E9E9E",
iconProfileColor: "#C4C4C4"
} }

Loading…
Откажи
Сачувај