| }, | }, | ||||
| "dependencies": { | "dependencies": { | ||||
| "@babel/helper-annotate-as-pure": { | "@babel/helper-annotate-as-pure": { | ||||
| "version": "7.16.7", | |||||
| "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", | |||||
| "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", | |||||
| "version": "7.18.6", | |||||
| "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", | |||||
| "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", | |||||
| "requires": { | "requires": { | ||||
| "@babel/types": "^7.16.7" | |||||
| "@babel/types": "^7.18.6" | |||||
| } | } | ||||
| }, | }, | ||||
| "@babel/helper-module-imports": { | "@babel/helper-module-imports": { | ||||
| "version": "7.16.7", | |||||
| "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", | |||||
| "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", | |||||
| "version": "7.18.6", | |||||
| "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", | |||||
| "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", | |||||
| "requires": { | "requires": { | ||||
| "@babel/types": "^7.16.7" | |||||
| "@babel/types": "^7.18.6" | |||||
| } | } | ||||
| }, | }, | ||||
| "@babel/helper-validator-identifier": { | "@babel/helper-validator-identifier": { | ||||
| "version": "7.16.7", | |||||
| "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", | |||||
| "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==" | |||||
| "version": "7.18.6", | |||||
| "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", | |||||
| "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==" | |||||
| }, | }, | ||||
| "@babel/types": { | "@babel/types": { | ||||
| "version": "7.18.4", | |||||
| "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.4.tgz", | |||||
| "integrity": "sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==", | |||||
| "version": "7.18.9", | |||||
| "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.9.tgz", | |||||
| "integrity": "sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==", | |||||
| "requires": { | "requires": { | ||||
| "@babel/helper-validator-identifier": "^7.16.7", | |||||
| "@babel/helper-validator-identifier": "^7.18.6", | |||||
| "to-fast-properties": "^2.0.0" | "to-fast-properties": "^2.0.0" | ||||
| } | } | ||||
| } | } |
| import React from 'react'; | |||||
| import { Router } from 'react-router-dom'; | |||||
| import { Helmet } from 'react-helmet-async'; | |||||
| import i18next from 'i18next'; | |||||
| import history from './store/utils/history'; | |||||
| import AppRoutes from './AppRoutes'; | |||||
| import Header from './components/Header/Header'; | |||||
| import { StyledEngineProvider } from '@mui/material'; | |||||
| import GlobalStyle from './components/Styles/globalStyles'; | |||||
| import React from "react"; | |||||
| import { Router } from "react-router-dom"; | |||||
| import { Helmet } from "react-helmet-async"; | |||||
| import i18next from "i18next"; | |||||
| import history from "./store/utils/history"; | |||||
| import AppRoutes from "./AppRoutes"; | |||||
| import Header from "./components/Header/Header"; | |||||
| import { StyledEngineProvider } from "@mui/material"; | |||||
| import GlobalStyle from "./components/Styles/globalStyles"; | |||||
| const App = () => { | const App = () => { | ||||
| return ( | return ( | ||||
| <> | |||||
| <Router history={history}> | |||||
| <Helmet> | |||||
| <title>{i18next.t("app.title")}</title> | |||||
| </Helmet> | |||||
| <Header/> | |||||
| <StyledEngineProvider injectFirst> | |||||
| <GlobalStyle /> | |||||
| <AppRoutes /> | |||||
| </StyledEngineProvider> | |||||
| {/* </main> */} | |||||
| </Router> | |||||
| </> | |||||
| <Router history={history}> | |||||
| <Helmet> | |||||
| <title>{i18next.t("app.title")}</title> | |||||
| </Helmet> | |||||
| <StyledEngineProvider injectFirst> | |||||
| <Header /> | |||||
| <GlobalStyle /> | |||||
| <AppRoutes /> | |||||
| </StyledEngineProvider> | |||||
| {/* </main> */} | |||||
| </Router> | |||||
| ); | ); | ||||
| }; | }; | ||||
| import NotFoundPage from './pages/ErrorPages/NotFoundPage'; | import NotFoundPage from './pages/ErrorPages/NotFoundPage'; | ||||
| import ErrorPage from './pages/ErrorPages/ErrorPage'; | import ErrorPage from './pages/ErrorPages/ErrorPage'; | ||||
| import ForgotPasswordPage from './pages/ForgotPasswordPage/ForgotPasswordPage'; | import ForgotPasswordPage from './pages/ForgotPasswordPage/ForgotPasswordPage'; | ||||
| import PrivateRoute from './components/Router/PrivateRoute'; | |||||
| // import PrivateRoute from './components/Router/PrivateRoute'; | |||||
| import MailSent from './pages/ForgotPasswordPage/ForgotPasswordMailSent/MailSent'; | import MailSent from './pages/ForgotPasswordPage/ForgotPasswordMailSent/MailSent'; | ||||
| import Register from './pages/RegisterPages/Register/Register'; | import Register from './pages/RegisterPages/Register/Register'; | ||||
| import RegisterSuccessful from './pages/RegisterPages/RegisterSuccessful.js/RegisterSuccessful'; | import RegisterSuccessful from './pages/RegisterPages/RegisterSuccessful.js/RegisterSuccessful'; | ||||
| <Route path={FORGOT_PASSWORD_PAGE} component={ForgotPasswordPage} /> | <Route path={FORGOT_PASSWORD_PAGE} component={ForgotPasswordPage} /> | ||||
| <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}/> | ||||
| <PrivateRoute | |||||
| <Route path={HOME_PAGE} component={HomePage} /> | |||||
| {/* <PrivateRoute | |||||
| exact | exact | ||||
| path={HOME_PAGE} | path={HOME_PAGE} | ||||
| component={HomePage} | component={HomePage} | ||||
| /> | |||||
| /> */} | |||||
| <Redirect from="*" to={NOT_FOUND_PAGE} /> | <Redirect from="*" to={NOT_FOUND_PAGE} /> | ||||
| </Switch> | </Switch> | ||||
| )}; | )}; |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | |||||
| <path d="M22 3H2L10 12.46V19L14 21V12.46L22 3Z" stroke="#5A3984" stroke-width="1.28" stroke-linecap="round" stroke-linejoin="round"/> | |||||
| </svg> |
| <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"> | |||||
| <path d="M6.75 15.75H3.75C3.35218 15.75 2.97064 15.592 2.68934 15.3107C2.40804 15.0294 2.25 14.6478 2.25 14.25V3.75C2.25 3.35218 2.40804 2.97064 2.68934 2.68934C2.97064 2.40804 3.35218 2.25 3.75 2.25H6.75" stroke="#FEB005" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/> | |||||
| <path d="M12 12.75L15.75 9L12 5.25" stroke="#FEB005" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/> | |||||
| <path d="M15.75 9H6.75" stroke="#FEB005" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/> | |||||
| </svg> |
| -webkit-font-smoothing: antialiased; | -webkit-font-smoothing: antialiased; | ||||
| -moz-osx-font-smoothing: grayscale; | -moz-osx-font-smoothing: grayscale; | ||||
| overflow-anchor: none; | overflow-anchor: none; | ||||
| background-color: #F1F1F1; | |||||
| background-color: #F5F5F5; | |||||
| } | } | ||||
| * { | * { |
| import selectedTheme from "../../../themes"; | import selectedTheme from "../../../themes"; | ||||
| import useFilters from "../../../hooks/useFilters"; | import useFilters from "../../../hooks/useFilters"; | ||||
| const FilterCard = () => { | |||||
| const FilterCard = (props) => { | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const [isOpened, setIsOpened] = useState(false); | const [isOpened, setIsOpened] = useState(false); | ||||
| const [isDisabled, setIsDisabled] = useState(true); | const [isDisabled, setIsDisabled] = useState(true); | ||||
| } else { | } else { | ||||
| setIsDisabled(false); | setIsDisabled(false); | ||||
| } | } | ||||
| }, [filters.selectedCategory]) | |||||
| }, [filters.selectedCategory]); | |||||
| const handleSelectCategory = (category) => { | const handleSelectCategory = (category) => { | ||||
| filters.setSelectedCategory(category); | filters.setSelectedCategory(category); | ||||
| filters.setSelectedSubcategory(); | filters.setSelectedSubcategory(); | ||||
| } | |||||
| }; | |||||
| const handleOpen = () => { | const handleOpen = () => { | ||||
| setIsOpened(prevState => !prevState); | |||||
| } | |||||
| setIsOpened((prevState) => !prevState); | |||||
| }; | |||||
| const handleFilters = () => { | const handleFilters = () => { | ||||
| filters.applyFilters(); | filters.applyFilters(); | ||||
| if (props.closeResponsive) props.closeResponsive(); | |||||
| }; | }; | ||||
| const clearFilters = () => { | const clearFilters = () => { | ||||
| filters.clearFilters(); | filters.clearFilters(); | ||||
| }; | }; | ||||
| return ( | return ( | ||||
| <FilterCardContainer> | |||||
| <FilterCardContainer responsiveOpen={props.responsiveOpen} responsive={props.responsive}> | |||||
| <Header> | <Header> | ||||
| <Title>{t("filters.title")}</Title> | <Title>{t("filters.title")}</Title> | ||||
| <Link | <Link | ||||
| /> | /> | ||||
| </ContentContainer> | </ContentContainer> | ||||
| <Footer> | |||||
| <Footer responsiveOpen={props.responsiveOpen}> | |||||
| {props.responsiveOpen && ( | |||||
| <PrimaryButton | |||||
| variant="outlined" | |||||
| fullWidth | |||||
| onClick={props.closeResponsive} | |||||
| textcolor={selectedTheme.primaryPurple} | |||||
| font="Open Sans" | |||||
| style={{ | |||||
| fontWeight: "600", | |||||
| fontSize: "12px", | |||||
| border: "0", | |||||
| textAlign: "center" | |||||
| }} | |||||
| > | |||||
| ZATVORI | |||||
| </PrimaryButton> | |||||
| )} | |||||
| <PrimaryButton | <PrimaryButton | ||||
| variant="outlined" | variant="outlined" | ||||
| fullWidth | fullWidth | ||||
| FilterCard.propTypes = { | FilterCard.propTypes = { | ||||
| children: PropTypes.node, | children: PropTypes.node, | ||||
| filters: PropTypes.any, | filters: PropTypes.any, | ||||
| responsive: PropTypes.bool, | |||||
| responsiveOpen: PropTypes.bool, | |||||
| closeResponsive: PropTypes.func, | |||||
| }; | }; | ||||
| FilterCard.defaultProps = { | |||||
| responsive: false, | |||||
| responsiveOpen: false, | |||||
| } | |||||
| export default FilterCard; | export default FilterCard; |
| export const FilterCardContainer = styled(Box)` | export const FilterCardContainer = styled(Box)` | ||||
| position: fixed; | position: fixed; | ||||
| box-sizing: border-box; | |||||
| border-radius: 0; | border-radius: 0; | ||||
| border-top-right-radius: 4px; | border-top-right-radius: 4px; | ||||
| height: calc(100% - 90px); | height: calc(100% - 90px); | ||||
| padding: 36px; | padding: 36px; | ||||
| background-color: white; | background-color: white; | ||||
| width: calc(100% / 12 * 2.4); | |||||
| width: calc(100% / 12 * 3.5); | |||||
| left: 0; | left: 0; | ||||
| display: flex; | |||||
| max-width: 360px; | |||||
| display: ${(props) => (props.responsive && !props.responsiveOpen ? "none" : "flex")}; | |||||
| flex-direction: column; | flex-direction: column; | ||||
| justify-content: space-between; | justify-content: space-between; | ||||
| background-color: white; | background-color: white; | ||||
| min-width: fit-content; | min-width: fit-content; | ||||
| min-width: 285px !important; | |||||
| z-index: 9; | z-index: 9; | ||||
| margin-top: -24px; | margin-top: -24px; | ||||
| transition: all ease-in-out .36s; | |||||
| transition: all ease-in-out 0.36s; | |||||
| @media (max-width: 900px) { | @media (max-width: 900px) { | ||||
| margin-left: -400px; | margin-left: -400px; | ||||
| transition: all ease-in-out .36s; | |||||
| ${(props) => | |||||
| props.responsiveOpen | |||||
| ? ` | |||||
| display: "flex"; | |||||
| margin-left: 0; | |||||
| max-width: 100vw; | |||||
| width: 100vw; | |||||
| bottom: 0; | |||||
| height: calc(100% - 50px); | |||||
| ` : "display: none"}; | |||||
| transition: all ease-in-out 0.36s; | |||||
| } | } | ||||
| @media (max-width: 600px) { | @media (max-width: 600px) { | ||||
| margin-top: -14px; | margin-top: -14px; | ||||
| export const Footer = styled(Box)` | export const Footer = styled(Box)` | ||||
| position: "sticky"; | position: "sticky"; | ||||
| ${(props) => | |||||
| props.responsiveOpen && | |||||
| ` | |||||
| flex-direction: row; | |||||
| display: flex; | |||||
| justify-content: space-around;`} | |||||
| bottom: 0; | bottom: 0; | ||||
| & div button { | & div button { | ||||
| height: 48px; | height: 48px; | ||||
| } | } | ||||
| scrollbar-width: thin; | scrollbar-width: thin; | ||||
| scrollbar-color: #ddd; | scrollbar-color: #ddd; | ||||
| ${() => window.scrollbars.visible && `padding-right: 15px;`} | |||||
| ${() => window.scrollbars.visible && `padding-right: 15px`}; | |||||
| `; | `; |
| CheckButton, | CheckButton, | ||||
| DetailIcon, | DetailIcon, | ||||
| DetailText, | DetailText, | ||||
| EyeIcon, | |||||
| Line, | Line, | ||||
| MessageIcon, | MessageIcon, | ||||
| OfferAuthor, | OfferAuthor, | ||||
| OfferDescriptionText, | OfferDescriptionText, | ||||
| OfferDescriptionTitle, | OfferDescriptionTitle, | ||||
| OfferDetails, | OfferDetails, | ||||
| OfferFlexContainer, | |||||
| OfferImage, | OfferImage, | ||||
| OfferImageContainer, | |||||
| OfferInfo, | OfferInfo, | ||||
| OfferLocation, | OfferLocation, | ||||
| OfferTitle, | OfferTitle, | ||||
| OfferTitleAboveImage, | |||||
| OfferViews, | OfferViews, | ||||
| } from "./OfferCard.styled"; | } from "./OfferCard.styled"; | ||||
| import { ReactComponent as Category } from "../../../assets/images/svg/category.svg"; | import { ReactComponent as Category } from "../../../assets/images/svg/category.svg"; | ||||
| import { ReactComponent as Eye } from "../../../assets/images/svg/eye-striked.svg"; | |||||
| import { ReactComponent as Message } from "../../../assets/images/svg/mail.svg"; | import { ReactComponent as Message } from "../../../assets/images/svg/mail.svg"; | ||||
| import selectedTheme from "../../../themes"; | import selectedTheme from "../../../themes"; | ||||
| const OfferCard = (props) => { | const OfferCard = (props) => { | ||||
| return ( | return ( | ||||
| <OfferCardContainer sponsored={props.offer.pinned.toString()} halfwidth={props.halfwidth ? 1 : 0}> | |||||
| <OfferImage src={props.offer.images[0]}></OfferImage> | |||||
| <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> | <OfferInfo> | ||||
| <OfferTitle>{props.offer.name}</OfferTitle> | <OfferTitle>{props.offer.name}</OfferTitle> | ||||
| <OfferAuthor> | <OfferAuthor> | ||||
| </OfferCategory> | </OfferCategory> | ||||
| <OfferViews> | <OfferViews> | ||||
| <DetailIcon color="black" component="span" size="16px"> | <DetailIcon color="black" component="span" size="16px"> | ||||
| <Eye width={"12px"} height={"11px"} /> | |||||
| <EyeIcon /> | |||||
| </DetailIcon> | </DetailIcon> | ||||
| <DetailText>{props.offer.views.viewers.length}</DetailText> | <DetailText>{props.offer.views.viewers.length}</DetailText> | ||||
| </OfferViews> | </OfferViews> | ||||
| </OfferInfo> | </OfferInfo> | ||||
| {!props.halfwidth ? ( | {!props.halfwidth ? ( | ||||
| <React.Fragment> | <React.Fragment> | ||||
| <Line/> | |||||
| <Line /> | |||||
| <OfferDescription> | <OfferDescription> | ||||
| <OfferDescriptionTitle>Opis:</OfferDescriptionTitle> | <OfferDescriptionTitle>Opis:</OfferDescriptionTitle> | ||||
| <OfferDescriptionText>{props.offer.description}</OfferDescriptionText> | |||||
| <OfferDescriptionText> | |||||
| {props.offer.description} | |||||
| </OfferDescriptionText> | |||||
| </OfferDescription> | </OfferDescription> | ||||
| <CheckButton | <CheckButton | ||||
| variant={props.sponsored ? "contained" : "outlined"} | variant={props.sponsored ? "contained" : "outlined"} | ||||
| buttoncolor={selectedTheme.primaryPurple} | buttoncolor={selectedTheme.primaryPurple} | ||||
| textcolor={props.sponsored ? "white" : selectedTheme.primaryPurple} | textcolor={props.sponsored ? "white" : selectedTheme.primaryPurple} | ||||
| style={{fontWeight: "600"}} | |||||
| style={{ fontWeight: "600" }} | |||||
| > | > | ||||
| Pogledaj proizvod | Pogledaj proizvod | ||||
| </CheckButton> | </CheckButton> | ||||
| {props.quantity} | {props.quantity} | ||||
| {props.package} | {props.package} | ||||
| {props.numberOfViews} */} | {props.numberOfViews} */} | ||||
| </OfferFlexContainer> | |||||
| </OfferCardContainer> | </OfferCardContainer> | ||||
| </React.Fragment> | |||||
| ); | ); | ||||
| }; | }; | ||||
| 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 { ReactComponent as Eye } from "../../../assets/images/svg/eye-striked.svg"; | |||||
| export const OfferCardContainer = styled(Container)` | export const OfferCardContainer = styled(Container)` | ||||
| display: flex; | display: flex; | ||||
| flex-direction: row; | |||||
| flex-direction: column; | |||||
| width: ${(props) => (!props.halfwidth ? "100%" : "49%")}; | width: ${(props) => (!props.halfwidth ? "100%" : "49%")}; | ||||
| 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; | ||||
| ${(props) => | ${(props) => | ||||
| props.sponsored === 'true' && | |||||
| props.sponsored === "true" && | |||||
| `border: 1px solid ${selectedTheme.borderSponsoredColor};`} | `border: 1px solid ${selectedTheme.borderSponsoredColor};`} | ||||
| padding: 16px; | padding: 16px; | ||||
| max-width: 2000px; | max-width: 2000px; | ||||
| height: 180px; | height: 180px; | ||||
| position: relative; | position: relative; | ||||
| @media (max-width: 550px) { | |||||
| height: 184px; | |||||
| padding: 18px; | |||||
| padding-top: 12px; | |||||
| } | |||||
| `; | |||||
| export const OfferFlexContainer = styled(Container)` | |||||
| display: flex; | |||||
| flex-direction: row; | |||||
| margin: 0; | |||||
| padding: 0; | |||||
| max-height: 184px; | |||||
| `; | `; | ||||
| export const OfferImage = styled.img` | export const OfferImage = styled.img` | ||||
| max-width: 144px; | max-width: 144px; | ||||
| max-height: 144px; | max-height: 144px; | ||||
| width: 144px; | |||||
| height: 144px; | |||||
| @media (max-width: 600px) { | |||||
| max-width: 108px; | |||||
| max-height: 108px; | |||||
| width: 108px; | |||||
| height: 108px; | |||||
| } | |||||
| `; | `; | ||||
| export const OfferInfo = styled(Box)` | export const OfferInfo = styled(Box)` | ||||
| display: flex; | display: flex; | ||||
| color: ${selectedTheme.primaryPurple}; | color: ${selectedTheme.primaryPurple}; | ||||
| font-weight: 700; | font-weight: 700; | ||||
| font-size: 24px; | font-size: 24px; | ||||
| @media (max-width: 550px) { | |||||
| font-size: 18px; | |||||
| display: none; | |||||
| } | |||||
| `; | `; | ||||
| export const OfferAuthor = styled(Box)` | export const OfferAuthor = styled(Box)` | ||||
| display: flex; | display: flex; | ||||
| font-family: "Open Sans"; | font-family: "Open Sans"; | ||||
| line-height: 22px; | line-height: 22px; | ||||
| font-size: 16px; | font-size: 16px; | ||||
| color: ${selectedTheme.primaryDarkText}; | |||||
| color: ${selectedTheme.primaryText}; | |||||
| @media (max-width: 600px) { | |||||
| font-size: 14px; | |||||
| position: relative; | |||||
| left: -1px; | |||||
| } | |||||
| `; | `; | ||||
| export const OfferLocation = styled(Typography)` | export const OfferLocation = styled(Typography)` | ||||
| font-family: "Open Sans"; | font-family: "Open Sans"; | ||||
| color: ${selectedTheme.primaryText}; | |||||
| color: ${selectedTheme.primaryDarkText}; | |||||
| line-height: 16px; | line-height: 16px; | ||||
| font-size: 12px; | font-size: 12px; | ||||
| `; | `; | ||||
| flex-wrap: ${(props) => (!props.halfwidth ? "no-wrap" : "wrap")}; | flex-wrap: ${(props) => (!props.halfwidth ? "no-wrap" : "wrap")}; | ||||
| justify-content: start; | justify-content: start; | ||||
| gap: 1rem; | gap: 1rem; | ||||
| @media (max-width: 650px) { | |||||
| flex-direction: column; | |||||
| justify-content: center; | |||||
| gap: 0; | |||||
| } | |||||
| `; | `; | ||||
| export const OfferCategory = styled(Box)` | export const OfferCategory = 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; | ||||
| @media (max-width: 1000px) { | |||||
| display: none; | |||||
| } | |||||
| `; | `; | ||||
| 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; | ||||
| @media (max-width: 1200px) { | |||||
| display: none; | |||||
| } | |||||
| `; | `; | ||||
| export const OfferDescriptionTitle = styled(Box)` | export const OfferDescriptionTitle = styled(Box)` | ||||
| font-family: "Open Sans"; | font-family: "Open Sans"; | ||||
| border-radius: 100%; | border-radius: 100%; | ||||
| padding-top: 2px; | padding-top: 2px; | ||||
| text-align: center; | text-align: center; | ||||
| @media (max-width: 600px) { | |||||
| width: 30px; | |||||
| height: 30px; | |||||
| top: 16px; | |||||
| right: 16px; | |||||
| padding: 0; | |||||
| & button svg { | |||||
| width: 16px; | |||||
| height: 16px; | |||||
| position: relative; | |||||
| top: -3px; | |||||
| left: -2.4px; | |||||
| } | |||||
| } | |||||
| `; | |||||
| export const OfferImageContainer = styled(Box)` | |||||
| min-width: 144px; | |||||
| min-height: 144px; | |||||
| width: 144px; | |||||
| height: 144px; | |||||
| @media (max-width: 600px) { | |||||
| min-width: 108px; | |||||
| min-height: 108px; | |||||
| width: 108px; | |||||
| height: 108px; | |||||
| border-radius: 4px; | |||||
| overflow: hidden; | |||||
| box-shadow: 4px 4px 9px rgba(0, 0, 0, 0.12); | |||||
| } | |||||
| `; | |||||
| export const OfferTitleAboveImage = styled(OfferTitle)` | |||||
| padding-bottom: 12px; | |||||
| padding-top: 5px; | |||||
| padding-left: 1px; | |||||
| display: block; | |||||
| @media (min-width: 551px) { | |||||
| display: none; | |||||
| } | |||||
| `; | |||||
| export const EyeIcon = styled(Eye)` | |||||
| width: 12px; | |||||
| height: 11px; | |||||
| @media (max-width: 600px) { | |||||
| position: relative; | |||||
| top: 1px !important; | |||||
| } | |||||
| `; | `; |
| import React, { useState, useMemo, useEffect, useRef } from "react"; | import React, { useState, useMemo, useEffect, useRef } from "react"; | ||||
| import { | import { | ||||
| AddOfferButton, | AddOfferButton, | ||||
| AuthButtonsContainer, | |||||
| AuthButtonsDrawerContainer, | |||||
| DrawerContainer, | DrawerContainer, | ||||
| EndIcon, | |||||
| FilterContainer, | |||||
| FilterIcon, | |||||
| HeaderContainer, | |||||
| LoginButton, | |||||
| LogoContainer, | LogoContainer, | ||||
| RegisterButton, | |||||
| SearchIcon, | SearchIcon, | ||||
| SearchInput, | SearchInput, | ||||
| SearchInputMobile, | |||||
| ToggleDrawerButton, | ToggleDrawerButton, | ||||
| ToolsButtonsContainer, | ToolsButtonsContainer, | ||||
| ToolsContainer, | ToolsContainer, | ||||
| UserButton, | UserButton, | ||||
| UserName, | UserName, | ||||
| } from "./Header.styled"; | } from "./Header.styled"; | ||||
| import PropTypes from "prop-types"; | |||||
| import { | import { | ||||
| AppBar, | AppBar, | ||||
| Badge, | Badge, | ||||
| import selectedTheme from "../../themes"; | import selectedTheme from "../../themes"; | ||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import { IconButton } from "../Buttons/IconButton/IconButton"; | import { IconButton } from "../Buttons/IconButton/IconButton"; | ||||
| import { Icon } from "../Icon/Icon"; | |||||
| import { useSelector } from "react-redux"; | |||||
| import { selectJWTToken } from "../../store/selectors/loginSelectors"; | |||||
| import { useDispatch, useSelector } from "react-redux"; | |||||
| 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 { 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"; | |||||
| const Header = () => { | const Header = () => { | ||||
| const [openDrawer, setOpenDrawer] = useState(false); | const [openDrawer, setOpenDrawer] = useState(false); | ||||
| const [openFilters, setOpenFilters] = useState(false); | |||||
| const [numberOfFilters, setNumberOfFilters] = useState(0); | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const searchRef = useRef(null); | const searchRef = useRef(null); | ||||
| const matches = useMediaQuery(theme.breakpoints.down("md")); | const matches = useMediaQuery(theme.breakpoints.down("md")); | ||||
| const user = useSelector(selectJWTToken); | |||||
| const user = useSelector(selectUserId); | |||||
| const search = useSearch(); | const search = useSearch(); | ||||
| const dispatch = useDispatch(); | |||||
| const name = useSelector(selectProfileName); | const name = useSelector(selectProfileName); | ||||
| const history = useHistory(); | |||||
| const routeMatch = useRouteMatch(); | |||||
| const filters = useFilters(); | |||||
| const searchMobileRef = useRef(null); | |||||
| const queryStringHook = useQueryString(); | |||||
| useEffect(() => { | |||||
| if (user?.length > 1) { | |||||
| dispatch(fetchProfile(user)); | |||||
| } | |||||
| }, [user]); | |||||
| useEffect(() => { | |||||
| setUserPopoverOpen(false); | |||||
| setUserAnchorEl(null); | |||||
| return () => { | |||||
| setUserPopoverOpen(false); | |||||
| setUserAnchorEl(null); | |||||
| }; | |||||
| }, []); | |||||
| useEffect(() => { | |||||
| setNumberOfFilters(filters.calculateFiltersChosen()); | |||||
| }, [ | |||||
| filters.selectedCategory, | |||||
| filters.selectedLocations, | |||||
| filters.selectedSubcategory, | |||||
| ]); | |||||
| const handleToggleDrawer = () => { | |||||
| setOpenDrawer(!openDrawer); | |||||
| }; | |||||
| useEffect(() => { | |||||
| if (queryStringHook.loadedFromURL) { | |||||
| const queryObject = new URLSearchParams( | |||||
| convertQueryStringFrontend(queryStringHook.queryString) | |||||
| ); | |||||
| if (queryObject.has("search")) { | |||||
| searchRef.current.value = queryObject.get("search"); | |||||
| searchMobileRef.current.value = queryObject.get("search"); | |||||
| } | |||||
| } | |||||
| }); | |||||
| const [postsPopoverOpen, setPostsPopoverOpen] = useState(false); | const [postsPopoverOpen, setPostsPopoverOpen] = useState(false); | ||||
| const [postsAnchorEl, setPostsAnchorEl] = useState(null); | const [postsAnchorEl, setPostsAnchorEl] = useState(null); | ||||
| location.pathname === "/login" || | location.pathname === "/login" || | ||||
| location.pathname === "/register" || | location.pathname === "/register" || | ||||
| location.pathname === "/forgot-password" || | location.pathname === "/forgot-password" || | ||||
| location.pathname === "/reset-password" | |||||
| location.pathname === "/reset-password" || | |||||
| location.pathname === "/" | |||||
| ) { | ) { | ||||
| shouldShowHeader = false; | shouldShowHeader = false; | ||||
| } | } | ||||
| if (location.pathname === "/" && user.JwtToken?.length === 0) { | |||||
| if (location.pathname === "/" && user?.length === 0) { | |||||
| shouldShowHeader = false; | shouldShowHeader = false; | ||||
| } | } | ||||
| setShouldShow(shouldShowHeader); | setShouldShow(shouldShowHeader); | ||||
| }, [location, user]); | |||||
| }, [location.pathname, user, routeMatch]); | |||||
| let listener; | |||||
| const handleFocusSearch = () => { | |||||
| listener = (event) => { | |||||
| if (event.keyCode === 13) { | |||||
| event.preventDefault(); | |||||
| search.searchOffers(searchRef.current.value) | |||||
| } | |||||
| } | |||||
| searchRef.current.addEventListener('keyup', listener); | |||||
| const handleToggleDrawer = () => { | |||||
| setOpenDrawer(!openDrawer); | |||||
| }; | }; | ||||
| const handleBlurSearch = () => { | |||||
| searchRef.current.removeEventListener('keyup', listener); | |||||
| const handleNavigateLogin = () => { | |||||
| setShouldShow(false); | |||||
| history.push(LOGIN_PAGE); | |||||
| }; | |||||
| const handleNavigateRegister = () => { | |||||
| setShouldShow(false); | |||||
| history.push(REGISTER_PAGE); | |||||
| }; | }; | ||||
| const drawerContent = useMemo( | const drawerContent = useMemo( | ||||
| () => ( | () => ( | ||||
| <DrawerContainer> | <DrawerContainer> | ||||
| <PrimaryButton | |||||
| type="submit" | |||||
| variant="contained" | |||||
| height="36px" | |||||
| fullWidth | |||||
| buttoncolor={selectedTheme.primaryYellow} | |||||
| textcolor="black" | |||||
| onClick={() => handleToggleDrawer()} | |||||
| > | |||||
| {t("header.addOffer")} | |||||
| </PrimaryButton> | |||||
| <ToolsContainer mobile> | |||||
| <IconButton | |||||
| onClick={(e) => { | |||||
| setPostsPopoverOpen(true); | |||||
| setPostsAnchorEl(e.currentTarget); | |||||
| }} | |||||
| sx={{ borderRadius: "4px" }} | |||||
| > | |||||
| <Autorenew /> | |||||
| <Typography sx={{ ml: 2 }}>Moje objave</Typography> | |||||
| </IconButton> | |||||
| <IconButton | |||||
| onClick={(e) => { | |||||
| setMsgPopoverOpen(true); | |||||
| setMsgAnchorEl(e.currentTarget); | |||||
| }} | |||||
| sx={{ borderRadius: "4px" }} | |||||
| > | |||||
| <Badge badgeContent={3} color="primary"> | |||||
| <MailIcon color="action" /> | |||||
| </Badge> | |||||
| <Typography sx={{ ml: 2 }}>Moje poruke</Typography> | |||||
| </IconButton> | |||||
| <IconButton | |||||
| onClick={(e) => { | |||||
| setUserPopoverOpen(true); | |||||
| setUserAnchorEl(e.currentTarget); | |||||
| }} | |||||
| sx={{ borderRadius: "4px" }} | |||||
| > | |||||
| <AccountCircle /> | |||||
| <Typography sx={{ ml: 2 }}>Moj profil</Typography> | |||||
| </IconButton> | |||||
| </ToolsContainer> | |||||
| {user ? ( | |||||
| <React.Fragment> | |||||
| <PrimaryButton | |||||
| type="submit" | |||||
| variant="contained" | |||||
| height="36px" | |||||
| fullWidth | |||||
| buttoncolor={selectedTheme.primaryYellow} | |||||
| textcolor="black" | |||||
| onClick={() => handleToggleDrawer()} | |||||
| > | |||||
| {t("header.addOffer")} | |||||
| </PrimaryButton> | |||||
| <ToolsContainer mobile> | |||||
| <IconButton | |||||
| onClick={(e) => { | |||||
| setPostsPopoverOpen(true); | |||||
| setPostsAnchorEl(e.currentTarget); | |||||
| }} | |||||
| sx={{ borderRadius: "4px" }} | |||||
| > | |||||
| <Autorenew /> | |||||
| <Typography sx={{ ml: 2 }}>Moje objave</Typography> | |||||
| </IconButton> | |||||
| <IconButton | |||||
| onClick={(e) => { | |||||
| setMsgPopoverOpen(true); | |||||
| setMsgAnchorEl(e.currentTarget); | |||||
| }} | |||||
| sx={{ borderRadius: "4px" }} | |||||
| > | |||||
| <Badge badgeContent={3} color="primary"> | |||||
| <MailIcon color="action" /> | |||||
| </Badge> | |||||
| <Typography sx={{ ml: 2 }}>Moje poruke</Typography> | |||||
| </IconButton> | |||||
| <IconButton | |||||
| onClick={(e) => { | |||||
| setUserPopoverOpen(true); | |||||
| setUserAnchorEl(e.currentTarget); | |||||
| }} | |||||
| sx={{ borderRadius: "4px" }} | |||||
| > | |||||
| <AccountCircle /> | |||||
| <Typography sx={{ ml: 2 }}>Moj profil</Typography> | |||||
| </IconButton> | |||||
| </ToolsContainer> | |||||
| </React.Fragment> | |||||
| ) : ( | |||||
| <AuthButtonsDrawerContainer> | |||||
| <RegisterButton | |||||
| type="submit" | |||||
| variant="contained" | |||||
| height="36px" | |||||
| fullWidth | |||||
| buttoncolor={selectedTheme.primaryYellow} | |||||
| textcolor={selectedTheme.primaryDarkText} | |||||
| onClick={handleNavigateRegister} | |||||
| > | |||||
| {t("register.headerTitle")} | |||||
| </RegisterButton> | |||||
| <LoginButton | |||||
| type="submit" | |||||
| variant="contained" | |||||
| height="36px" | |||||
| fullWidth | |||||
| buttoncolor={selectedTheme.primaryPurple} | |||||
| textcolor={selectedTheme.primaryIconBackgroundColor} | |||||
| onClick={handleNavigateLogin} | |||||
| > | |||||
| {t("login.headerTitle")} | |||||
| </LoginButton> | |||||
| </AuthButtonsDrawerContainer> | |||||
| )} | |||||
| </DrawerContainer> | </DrawerContainer> | ||||
| ), | ), | ||||
| [handleToggleDrawer] | [handleToggleDrawer] | ||||
| ); | ); | ||||
| let listener; | |||||
| const handleFocusSearch = () => { | |||||
| listener = (event) => { | |||||
| if (event.keyCode === 13) { | |||||
| event.preventDefault(); | |||||
| handleSearch(searchRef.current.value); | |||||
| } | |||||
| }; | |||||
| searchRef.current.addEventListener("keyup", listener); | |||||
| }; | |||||
| const handleBlurSearch = () => { | |||||
| searchRef.current.removeEventListener("keyup", listener); | |||||
| }; | |||||
| const handleSearch = (value) => { | |||||
| console.log(value); | |||||
| if (value.length === 0) return; | |||||
| search.searchOffers(value); | |||||
| }; | |||||
| const toggleFilters = () => { | |||||
| setOpenFilters((prevState) => !prevState); | |||||
| }; | |||||
| return ( | return ( | ||||
| <AppBar | |||||
| elevation={0} | |||||
| position="fixed" | |||||
| // positionFixed | |||||
| sx={{ backgroundColor: "white" }} | |||||
| style={{ display: shouldShow ? "flex" : "none" }} | |||||
| > | |||||
| <Toolbar> | |||||
| <ToolsContainer> | |||||
| <LogoContainer> | |||||
| <LogoHorizontal /> | |||||
| </LogoContainer> | |||||
| {matches && ( | |||||
| <Drawer | |||||
| open={openDrawer} | |||||
| toggleOpen={handleToggleDrawer} | |||||
| content={drawerContent} | |||||
| <HeaderContainer style={{ display: shouldShow ? "block" : "none" }}> | |||||
| <AppBar | |||||
| elevation={0} | |||||
| position="fixed" | |||||
| // positionFixed | |||||
| sx={{ backgroundColor: "white" }} | |||||
| > | |||||
| <Toolbar> | |||||
| <ToolsContainer> | |||||
| <LogoContainer> | |||||
| <LogoHorizontal /> | |||||
| </LogoContainer> | |||||
| {matches && ( | |||||
| <Drawer | |||||
| open={openDrawer} | |||||
| toggleOpen={handleToggleDrawer} | |||||
| content={drawerContent} | |||||
| /> | |||||
| )} | |||||
| <SearchInput | |||||
| fullWidth | |||||
| InputProps={{ | |||||
| endAdornment: ( | |||||
| <EndIcon size="36px"> | |||||
| <SearchIcon | |||||
| onClick={() => handleSearch(searchRef.current.value)} | |||||
| /> | |||||
| </EndIcon> | |||||
| ), | |||||
| }} | |||||
| placeholder={t("header.searchOffers")} | |||||
| onFocus={handleFocusSearch} | |||||
| onBlur={handleBlurSearch} | |||||
| ref={searchRef} | |||||
| /> | /> | ||||
| )} | |||||
| <SearchInput | |||||
| fullWidth | |||||
| InputProps={{ | |||||
| startAdornment: ( | |||||
| <Icon size="36px"> | |||||
| <SearchIcon /> | |||||
| </Icon> | |||||
| ), | |||||
| }} | |||||
| label={t("header.searchOffers")} | |||||
| onFocus={handleFocusSearch} | |||||
| onBlur={handleBlurSearch} | |||||
| ref={searchRef} | |||||
| /> | |||||
| <ToolsButtonsContainer mobile={matches}> | |||||
| {matches ? ( | |||||
| <ToggleDrawerButton> | |||||
| <IconButton onClick={handleToggleDrawer}> | |||||
| <MenuOutlinedIcon /> | |||||
| </IconButton> | |||||
| </ToggleDrawerButton> | |||||
| {user ? ( | |||||
| <ToolsButtonsContainer mobile={matches}> | |||||
| {matches ? ( | |||||
| <ToggleDrawerButton> | |||||
| <IconButton onClick={handleToggleDrawer}> | |||||
| <MenuOutlinedIcon /> | |||||
| </IconButton> | |||||
| </ToggleDrawerButton> | |||||
| ) : ( | |||||
| <React.Fragment> | |||||
| <AddOfferButton | |||||
| type="submit" | |||||
| variant="contained" | |||||
| fullWidth | |||||
| buttoncolor={selectedTheme.primaryYellow} | |||||
| textcolor={selectedTheme.primaryDarkText} | |||||
| onClick={() => { | |||||
| setUserPopoverOpen(false); | |||||
| setUserAnchorEl(null); | |||||
| }} | |||||
| > | |||||
| {t("header.addOffer")} | |||||
| </AddOfferButton> | |||||
| <IconButton | |||||
| onClick={(e) => { | |||||
| setPostsPopoverOpen(true); | |||||
| setPostsAnchorEl(e.currentTarget); | |||||
| }} | |||||
| style={{ | |||||
| background: selectedTheme.primaryIconBackgroundColor, | |||||
| color: selectedTheme.primaryPurple, | |||||
| }} | |||||
| > | |||||
| <Autorenew /> | |||||
| </IconButton> | |||||
| <IconButton | |||||
| onClick={(e) => { | |||||
| setMsgPopoverOpen(true); | |||||
| setMsgAnchorEl(e.currentTarget); | |||||
| }} | |||||
| style={{ | |||||
| background: selectedTheme.primaryIconBackgroundColor, | |||||
| color: selectedTheme.primaryPurple, | |||||
| }} | |||||
| > | |||||
| <Badge badgeContent={3} color="primary"> | |||||
| <MailIcon /> | |||||
| </Badge> | |||||
| </IconButton> | |||||
| <UserButton | |||||
| onClick={(e) => { | |||||
| setUserPopoverOpen(true); | |||||
| setUserAnchorEl(e.currentTarget); | |||||
| }} | |||||
| > | |||||
| <UserName>{name}</UserName> | |||||
| <IconButton | |||||
| style={{ | |||||
| background: selectedTheme.primaryIconBackgroundColor, | |||||
| color: selectedTheme.primaryPurple, | |||||
| }} | |||||
| > | |||||
| <AccountCircle /> | |||||
| </IconButton> | |||||
| </UserButton> | |||||
| </React.Fragment> | |||||
| )} | |||||
| </ToolsButtonsContainer> | |||||
| ) : ( | ) : ( | ||||
| <React.Fragment> | |||||
| <AddOfferButton | |||||
| type="submit" | |||||
| variant="contained" | |||||
| fullWidth | |||||
| buttoncolor={selectedTheme.primaryYellow} | |||||
| textcolor={selectedTheme.primaryDarkText} | |||||
| > | |||||
| {t("header.addOffer")} | |||||
| </AddOfferButton> | |||||
| <IconButton | |||||
| onClick={(e) => { | |||||
| setPostsPopoverOpen(true); | |||||
| setPostsAnchorEl(e.currentTarget); | |||||
| }} | |||||
| style={{ | |||||
| background: selectedTheme.primaryIconBackgroundColor, | |||||
| color: selectedTheme.primaryPurple, | |||||
| }} | |||||
| > | |||||
| <Autorenew /> | |||||
| </IconButton> | |||||
| <IconButton | |||||
| onClick={(e) => { | |||||
| setMsgPopoverOpen(true); | |||||
| setMsgAnchorEl(e.currentTarget); | |||||
| }} | |||||
| style={{ | |||||
| background: selectedTheme.primaryIconBackgroundColor, | |||||
| color: selectedTheme.primaryPurple, | |||||
| }} | |||||
| > | |||||
| <Badge badgeContent={3} color="primary"> | |||||
| <MailIcon /> | |||||
| </Badge> | |||||
| </IconButton> | |||||
| <UserButton | |||||
| onClick={(e) => { | |||||
| setUserPopoverOpen(true); | |||||
| setUserAnchorEl(e.currentTarget); | |||||
| }} | |||||
| > | |||||
| <UserName>{name}</UserName> | |||||
| <IconButton | |||||
| style={{ | |||||
| background: selectedTheme.primaryIconBackgroundColor, | |||||
| color: selectedTheme.primaryPurple, | |||||
| }} | |||||
| > | |||||
| <AccountCircle /> | |||||
| </IconButton> | |||||
| </UserButton> | |||||
| </React.Fragment> | |||||
| <AuthButtonsContainer mobile={matches}> | |||||
| {matches ? ( | |||||
| <ToggleDrawerButton> | |||||
| <IconButton onClick={handleToggleDrawer}> | |||||
| <MenuOutlinedIcon /> | |||||
| </IconButton> | |||||
| </ToggleDrawerButton> | |||||
| ) : ( | |||||
| <React.Fragment> | |||||
| <LoginButton | |||||
| type="submit" | |||||
| variant="contained" | |||||
| fullWidth | |||||
| buttoncolor={selectedTheme.primaryPurple} | |||||
| textcolor={selectedTheme.offerBackgroundColor} | |||||
| onClick={handleNavigateLogin} | |||||
| > | |||||
| {t("login.headerTitle")} | |||||
| </LoginButton> | |||||
| <RegisterButton | |||||
| type="submit" | |||||
| variant="contained" | |||||
| fullWidth | |||||
| buttoncolor={selectedTheme.primaryYellow} | |||||
| textcolor={selectedTheme.primaryDarkText} | |||||
| onClick={handleNavigateRegister} | |||||
| > | |||||
| {t("register.headerTitle")} | |||||
| </RegisterButton> | |||||
| </React.Fragment> | |||||
| )} | |||||
| </AuthButtonsContainer> | |||||
| )} | )} | ||||
| </ToolsButtonsContainer> | |||||
| </ToolsContainer> | |||||
| </Toolbar> | |||||
| <PopoverComponent | |||||
| anchorEl={postsAnchorEl} | |||||
| open={postsPopoverOpen} | |||||
| onClose={() => { | |||||
| setPostsPopoverOpen(false); | |||||
| setPostsAnchorEl(null); | |||||
| }} | |||||
| content={<MyPosts />} | |||||
| /> | |||||
| <PopoverComponent | |||||
| anchorEl={msgAnchorEl} | |||||
| open={msgPopoverOpen} | |||||
| onClose={() => { | |||||
| setMsgPopoverOpen(false); | |||||
| setMsgAnchorEl(null); | |||||
| </ToolsContainer> | |||||
| </Toolbar> | |||||
| {user && ( | |||||
| <React.Fragment> | |||||
| <PopoverComponent | |||||
| anchorEl={postsAnchorEl} | |||||
| open={postsPopoverOpen} | |||||
| onClose={() => { | |||||
| setPostsPopoverOpen(false); | |||||
| setPostsAnchorEl(null); | |||||
| }} | |||||
| content={<MyPosts />} | |||||
| /> | |||||
| <PopoverComponent | |||||
| anchorEl={msgAnchorEl} | |||||
| open={msgPopoverOpen} | |||||
| onClose={() => { | |||||
| setMsgPopoverOpen(false); | |||||
| setMsgAnchorEl(null); | |||||
| }} | |||||
| content={<MyMessages />} | |||||
| /> | |||||
| <PopoverComponent | |||||
| anchorEl={userAnchorEl} | |||||
| open={userPopoverOpen} | |||||
| onClose={() => { | |||||
| setUserPopoverOpen(false); | |||||
| setUserAnchorEl(null); | |||||
| }} | |||||
| content={<MyProfile />} | |||||
| /> | |||||
| </React.Fragment> | |||||
| )} | |||||
| </AppBar> | |||||
| <SearchInputMobile | |||||
| fullWidth | |||||
| ref={searchMobileRef} | |||||
| InputProps={{ | |||||
| endAdornment: ( | |||||
| <React.Fragment> | |||||
| <FilterContainer number={numberOfFilters}> | |||||
| <FilterIcon onClick={toggleFilters} /> | |||||
| </FilterContainer> | |||||
| <EndIcon size="36px"> | |||||
| <SearchIcon | |||||
| onClick={() => handleSearch(searchMobileRef.current.value)} | |||||
| /> | |||||
| </EndIcon> | |||||
| </React.Fragment> | |||||
| ), | |||||
| }} | }} | ||||
| content={<MyMessages />} | |||||
| placeholder={t("header.searchOffers")} | |||||
| italicPlaceholder | |||||
| onFocus={handleFocusSearch} | |||||
| onBlur={handleBlurSearch} | |||||
| /> | /> | ||||
| <PopoverComponent | |||||
| anchorEl={userAnchorEl} | |||||
| open={userPopoverOpen} | |||||
| onClose={() => { | |||||
| setUserPopoverOpen(false); | |||||
| setUserAnchorEl(null); | |||||
| }} | |||||
| content={<MyProfile />} | |||||
| <FilterCard | |||||
| responsive={true} | |||||
| responsiveOpen={openFilters} | |||||
| closeResponsive={toggleFilters} | |||||
| /> | /> | ||||
| </AppBar> | |||||
| </HeaderContainer> | |||||
| ); | ); | ||||
| }; | }; | ||||
| Header.propTypes = { | |||||
| isGrid: PropTypes.bool, | |||||
| }; | |||||
| export default Header; | export default Header; |
| import { PrimaryButton } from "../Buttons/PrimaryButton/PrimaryButton"; | import { PrimaryButton } from "../Buttons/PrimaryButton/PrimaryButton"; | ||||
| import { TextField } from "../TextFields/TextField/TextField"; | import { TextField } from "../TextFields/TextField/TextField"; | ||||
| import { ReactComponent as Search } from "../../assets/images/svg/magnifying-glass.svg"; | import { ReactComponent as Search } from "../../assets/images/svg/magnifying-glass.svg"; | ||||
| import { ReactComponent as Filter } from "../../assets/images/svg/filter.svg"; | |||||
| import selectedTheme from "../../themes"; | import selectedTheme from "../../themes"; | ||||
| import { Icon } from "../Icon/Icon"; | |||||
| import IconWithNumber from "../Icon/IconWithNumber/IconWithNumber"; | |||||
| export const SearchInput = styled(TextField)` | export const SearchInput = styled(TextField)` | ||||
| margin-left: 3.8rem; | |||||
| background-color: #f4f4f4; | background-color: #f4f4f4; | ||||
| width: 45%; | width: 45%; | ||||
| flex: 3; | |||||
| max-width: 520px; | max-width: 520px; | ||||
| margin-right: 30px; | margin-right: 30px; | ||||
| font-family: "Open Sans"; | font-family: "Open Sans"; | ||||
| @media (max-width: 1700px) { | |||||
| margin-left: 15%; | |||||
| } | |||||
| @media (max-width: 1550px) { | |||||
| margin-left: 15%; | |||||
| } | |||||
| @media (max-width: 1320px) { | |||||
| margin-left: 7%; | |||||
| } | |||||
| @media (max-width: 1100px) { | @media (max-width: 1100px) { | ||||
| width: 36%; | width: 36%; | ||||
| } | } | ||||
| @media (max-width: 1000px) { | @media (max-width: 1000px) { | ||||
| width: 54%; | |||||
| margin-left: 2.8rem; | |||||
| width: 36%; | |||||
| margin-left: 5%; | |||||
| margin-right: 10px; | margin-right: 10px; | ||||
| } | } | ||||
| @media (max-width: 600px) { | |||||
| width: 60%; | |||||
| margin-left: 2rem; | |||||
| @media (max-width: 550px) { | |||||
| display: none; | |||||
| width: 0; | |||||
| } | } | ||||
| `; | `; | ||||
| export const DrawerContainer = styled(Box)` | export const DrawerContainer = styled(Box)` | ||||
| export const ToolsContainer = styled(Box)` | export const ToolsContainer = styled(Box)` | ||||
| display: flex; | display: flex; | ||||
| flex-direction: row; | flex-direction: row; | ||||
| justify-content: ${(props) => (props.mobile ? "center" : "space-between")}; | justify-content: ${(props) => (props.mobile ? "center" : "space-between")}; | ||||
| align-items: ${(props) => (props.mobile ? "start" : "center")}; | align-items: ${(props) => (props.mobile ? "start" : "center")}; | ||||
| ${(props) => !props.mobile && `width: 100%;`} | ${(props) => !props.mobile && `width: 100%;`} | ||||
| & div button { | & div button { | ||||
| ${props => props.mobile && `width: auto;`} | |||||
| ${(props) => props.mobile && `width: auto;`} | |||||
| } | } | ||||
| `; | `; | ||||
| export const LogoContainer = styled(Box)` | export const LogoContainer = styled(Box)` | ||||
| display: flex; | display: flex; | ||||
| flex: 4; | flex: 4; | ||||
| justify-content: space-between; | justify-content: space-between; | ||||
| min-width: ${props => props.mobile ? "40px" : "600px"}; | |||||
| min-width: ${(props) => (props.mobile ? "40px" : "600px")}; | |||||
| max-width: 600px; | max-width: 600px; | ||||
| align-items: center; | align-items: center; | ||||
| flex-wrap: nowrap; | flex-wrap: nowrap; | ||||
| @media (max-width: 1400px) { | |||||
| min-width: 450px; | |||||
| } | |||||
| @media (max-width: 1200px) { | @media (max-width: 1200px) { | ||||
| min-width: 400px; | min-width: 400px; | ||||
| } | } | ||||
| @media (max-width: 950px) { | |||||
| min-width: 250px; | |||||
| } | |||||
| @media (max-width: 800px) { | |||||
| @media (max-width: 900px) { | |||||
| flex: 0.35; | |||||
| min-width: 0px; | min-width: 0px; | ||||
| width: 0px; | |||||
| width: 60px; | |||||
| justify-content: right; | |||||
| } | } | ||||
| `; | `; | ||||
| export const ToggleDrawerButton = styled(Box)` | export const ToggleDrawerButton = styled(Box)` | ||||
| width: 180px; | width: 180px; | ||||
| font-weight: 600; | font-weight: 600; | ||||
| `; | `; | ||||
| export const EndIcon = styled(Icon)``; | |||||
| export const SearchIcon = styled(Search)` | export const SearchIcon = styled(Search)` | ||||
| position: relative; | position: relative; | ||||
| top: 11px; | top: 11px; | ||||
| left: 4px; | left: 4px; | ||||
| cursor: pointer; | |||||
| color: ${selectedTheme.primaryPurple}; | color: ${selectedTheme.primaryPurple}; | ||||
| & path { | & path { | ||||
| width: 18px; | width: 18px; | ||||
| height: 18px; | height: 18px; | ||||
| } | } | ||||
| @media (max-width: 600px) { | |||||
| height: 14px; | |||||
| width: 14px; | |||||
| left: 11px; | |||||
| } | |||||
| `; | `; | ||||
| export const UserButton = styled(Box)` | export const UserButton = styled(Box)` | ||||
| display: flex; | display: flex; | ||||
| font-weight: 600; | font-weight: 600; | ||||
| white-space: nowrap; | white-space: nowrap; | ||||
| `; | `; | ||||
| export const RegisterButton = styled(PrimaryButton)` | |||||
| height: 49px; | |||||
| width: 180px; | |||||
| font-weight: 600; | |||||
| @media (max-width: 550px) { | |||||
| margin-bottom: 20px; | |||||
| } | |||||
| `; | |||||
| export const LoginButton = styled(PrimaryButton)` | |||||
| height: 49px; | |||||
| width: 180px; | |||||
| font-weight: 600; | |||||
| margin-right: 10px; | |||||
| `; | |||||
| export const AuthButtonsContainer = styled(Box)` | |||||
| display: flex; | |||||
| justify-content: flex-start; | |||||
| flex: 1; | |||||
| min-width: ${(props) => (props.mobile ? "40px" : "200px")}; | |||||
| max-width: 600px; | |||||
| align-items: flex-start; | |||||
| flex-wrap: nowrap; | |||||
| margin-left: 40px; | |||||
| & div { | |||||
| margin-left: 20px; | |||||
| } | |||||
| @media (max-width: 1300px) { | |||||
| margin-left: 0; | |||||
| } | |||||
| @media (max-width: 1200px) { | |||||
| min-width: 400px; | |||||
| } | |||||
| @media (max-width: 900px) { | |||||
| min-width: 0px; | |||||
| width: 0px; | |||||
| justify-content: right; | |||||
| } | |||||
| `; | |||||
| export const AuthButtonsDrawerContainer = styled(Box)` | |||||
| position: relative; | |||||
| left: 10px; | |||||
| height: 200px; | |||||
| display: flex; | |||||
| flex-direction: column; | |||||
| flex: 1; | |||||
| justify-content: space-around; | |||||
| `; | |||||
| export const SearchInputMobile = styled(SearchInput)` | |||||
| @media (max-width: 550px) { | |||||
| display: block; | |||||
| position: relative; | |||||
| width: 80%; | |||||
| top: 70px; | |||||
| height: 46px; | |||||
| left: -50px; | |||||
| font-family: "Open Sans"; | |||||
| & div { | |||||
| background-color: white; | |||||
| height: 40px; | |||||
| overflow: visible; | |||||
| & input { | |||||
| font-size: 14px !important; | |||||
| } | |||||
| } | |||||
| } | |||||
| @media (min-width: 551px) { | |||||
| display: none; | |||||
| width: 0; | |||||
| } | |||||
| `; | |||||
| export const FilterContainer = styled(IconWithNumber)` | |||||
| position: relative; | |||||
| top: 8px; | |||||
| left: 95px; | |||||
| cursor: pointer; | |||||
| background-color: ${selectedTheme.offerBackgroundColor} !important; | |||||
| & div { | |||||
| width: 16px; | |||||
| height: 16px; | |||||
| background-color: ${selectedTheme.primaryPurple}; | |||||
| position: absolute; | |||||
| top: -5px; | |||||
| right: -5px; | |||||
| line-height: 15px; | |||||
| text-align: center; | |||||
| padding-right: 2px; | |||||
| } | |||||
| `; | |||||
| export const FilterIcon = styled(Filter)` | |||||
| background-color: ${selectedTheme.offerBackgroundColor}; | |||||
| `; | |||||
| export const HeaderContainer = styled(Box)``; |
| const IconWithNumber = (props) => { | const IconWithNumber = (props) => { | ||||
| return ( | return ( | ||||
| <IconWithNumberContainer> | |||||
| <IconWithNumberContainer className={props.className}> | |||||
| {props.children} | {props.children} | ||||
| {props.number > 0 && <Number>{props.number}</Number>} | {props.number > 0 && <Number>{props.number}</Number>} | ||||
| </IconWithNumberContainer> | </IconWithNumberContainer> | ||||
| IconWithNumber.propTypes = { | IconWithNumber.propTypes = { | ||||
| children: PropTypes.node, | children: PropTypes.node, | ||||
| number: PropTypes.number, | number: PropTypes.number, | ||||
| className: PropTypes.string, | |||||
| } | } | ||||
| export default IconWithNumber | export default IconWithNumber |
| import React, { useEffect, useState } from "react"; | import React, { useEffect, useState } from "react"; | ||||
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { | import { | ||||
| HeaderAltLocation, | |||||
| HeaderButton, | HeaderButton, | ||||
| HeaderButtons, | HeaderButtons, | ||||
| HeaderContainer, | HeaderContainer, | ||||
| HeaderOptions, | HeaderOptions, | ||||
| HeaderSelect, | HeaderSelect, | ||||
| IconStyled, | IconStyled, | ||||
| SelectOption, | |||||
| } from "./Header.styled"; | } from "./Header.styled"; | ||||
| import { ReactComponent as GridSquare } from "../../../assets/images/svg/offer-grid-square.svg"; | import { ReactComponent as GridSquare } from "../../../assets/images/svg/offer-grid-square.svg"; | ||||
| import { ReactComponent as GridLine } from "../../../assets/images/svg/offer-grid-line.svg"; | import { ReactComponent as GridLine } from "../../../assets/images/svg/offer-grid-line.svg"; | ||||
| import { ReactComponent as Down } from "../../../assets/images/svg/down-arrow.svg"; | import { ReactComponent as Down } from "../../../assets/images/svg/down-arrow.svg"; | ||||
| import selectedTheme from "../../../themes"; | import selectedTheme from "../../../themes"; | ||||
| import Option from "../../Select/Option/Option"; | |||||
| import { sortEnum } from "../../../enums/sortEnum"; | import { sortEnum } from "../../../enums/sortEnum"; | ||||
| import useFilters from "../../../hooks/useFilters"; | import useFilters from "../../../hooks/useFilters"; | ||||
| import useSorting from "../../../hooks/useSorting"; | import useSorting from "../../../hooks/useSorting"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| import { Tooltip } from "@mui/material"; | |||||
| const DownArrow = (props) => ( | const DownArrow = (props) => ( | ||||
| <IconStyled {...props}> | <IconStyled {...props}> | ||||
| const Header = (props) => { | const Header = (props) => { | ||||
| const filters = useFilters(); | const filters = useFilters(); | ||||
| const sorting = useSorting(); | const sorting = useSorting(); | ||||
| const { t } = useTranslation(); | |||||
| const [sortOption, setSortOption] = useState(sortEnum.INITIAL); | const [sortOption, setSortOption] = useState(sortEnum.INITIAL); | ||||
| const [headerString, setHeaderString] = useState("SVE KATEGORIJE"); | const [headerString, setHeaderString] = useState("SVE KATEGORIJE"); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| if (filters.isApplied) { | if (filters.isApplied) { | ||||
| let headerStringLocal = "SVE KATEGORIJE"; | |||||
| let headerStringLocal = "Sve kategorije"; | |||||
| if (filters.selectedCategory?.name) { | if (filters.selectedCategory?.name) { | ||||
| headerStringLocal = filters.selectedCategory.name; | headerStringLocal = filters.selectedCategory.name; | ||||
| if (filters.selectedSubcategory?.name) { | if (filters.selectedSubcategory?.name) { | ||||
| return ( | return ( | ||||
| <HeaderContainer> | <HeaderContainer> | ||||
| <HeaderLocation>{headerString}</HeaderLocation> | |||||
| <Tooltip title={headerString}> | |||||
| {headerString === "Sve kategorije" && | |||||
| (sorting.selectedSortOption === sortEnum.INITIAL || | |||||
| sorting.selectedSortOption === sortEnum.NEW) ? ( | |||||
| <React.Fragment> | |||||
| <HeaderLocation initial>{headerString}</HeaderLocation> | |||||
| <HeaderAltLocation>{t("header.newOffers")}</HeaderAltLocation> | |||||
| </React.Fragment> | |||||
| ) : ( | |||||
| <HeaderLocation>{headerString}</HeaderLocation> | |||||
| )} | |||||
| </Tooltip> | |||||
| <HeaderOptions> | <HeaderOptions> | ||||
| <HeaderButtons> | <HeaderButtons> | ||||
| <HeaderButton | <HeaderButton | ||||
| <HeaderSelect | <HeaderSelect | ||||
| value={sortOption?.value ? sortOption.value : sortEnum.INITIAL.value} | value={sortOption?.value ? sortOption.value : sortEnum.INITIAL.value} | ||||
| IconComponent={DownArrow} | IconComponent={DownArrow} | ||||
| width="209px" | |||||
| height="34px" | |||||
| onChange={handleChangeSelect} | onChange={handleChangeSelect} | ||||
| > | > | ||||
| {Object.keys(sortEnum).map((property) => { | {Object.keys(sortEnum).map((property) => { | ||||
| return ( | return ( | ||||
| <Option | |||||
| <SelectOption | |||||
| value={sortEnum[property].value} | value={sortEnum[property].value} | ||||
| key={sortEnum[property].value} | key={sortEnum[property].value} | ||||
| style={{ | |||||
| display: sortEnum[property].value === 0 ? "none" : "flex", | |||||
| }} | |||||
| > | > | ||||
| {sortEnum[property].mainText} | {sortEnum[property].mainText} | ||||
| </Option> | |||||
| </SelectOption> | |||||
| ); | ); | ||||
| })} | })} | ||||
| </HeaderSelect> | </HeaderSelect> |
| import { Box, MenuItem } from "@mui/material"; | |||||
| import { Box, MenuItem, Typography } from "@mui/material"; | |||||
| import styled from "styled-components"; | import styled from "styled-components"; | ||||
| import selectedTheme from "../../../themes"; | import selectedTheme from "../../../themes"; | ||||
| import { IconButton } from "../../Buttons/IconButton/IconButton"; | import { IconButton } from "../../Buttons/IconButton/IconButton"; | ||||
| import Option from "../../Select/Option/Option"; | |||||
| import Select from "../../Select/Select"; | import Select from "../../Select/Select"; | ||||
| export const HeaderContainer = styled(Box)` | export const HeaderContainer = styled(Box)` | ||||
| margin-top: 20px; | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| align-items: center; | |||||
| ` | |||||
| margin-top: 20px; | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| align-items: center; | |||||
| `; | |||||
| export const HeaderLocation = styled(Box)` | export const HeaderLocation = styled(Box)` | ||||
| font-family: "Open Sans"; | |||||
| color: ${selectedTheme.primaryPurple}; | |||||
| font-weight: 700; | |||||
| line-height: 22px; | |||||
| font-size: 16px; | |||||
| flex: 2; | |||||
| ` | |||||
| export const HeaderButton = styled(IconButton)` | |||||
| padding: 2px 10px; | |||||
| @media (max-width: 1500px) { | |||||
| display: none; | |||||
| font-family: "Open Sans"; | |||||
| color: ${selectedTheme.primaryPurple}; | |||||
| font-weight: 700; | |||||
| line-height: 22px; | |||||
| font-size: 16px; | |||||
| flex: 2; | |||||
| max-width: ${props => props.initial ? "fit-content" : "50%"}; | |||||
| white-space: nowrap; | |||||
| overflow: hidden; | |||||
| text-overflow: ellipsis; | |||||
| &:after { | |||||
| content: ${props => props.initial ? `":"` : `""`}; | |||||
| @media (max-width: 600px) { | |||||
| content: ""; | |||||
| } | } | ||||
| ` | |||||
| } | |||||
| @media (max-width: 600px) { | |||||
| font-size: 14px; | |||||
| padding-top: 3px; | |||||
| } | |||||
| `; | |||||
| export const HeaderButton = styled(IconButton)` | |||||
| padding: 2px 10px; | |||||
| @media (max-width: 1500px) { | |||||
| display: none; | |||||
| } | |||||
| `; | |||||
| export const HeaderOptions = styled(Box)` | export const HeaderOptions = styled(Box)` | ||||
| display: flex; | |||||
| flex-direction: row; | |||||
| flex: 1; | |||||
| justify-content: end; | |||||
| ` | |||||
| display: flex; | |||||
| flex-direction: row; | |||||
| flex: 1; | |||||
| justify-content: end; | |||||
| `; | |||||
| export const HeaderSelect = styled(Select)` | export const HeaderSelect = styled(Select)` | ||||
| width: 210px; | |||||
| height: 35px; | |||||
| font-family: "Open Sans"; | |||||
| margin-top: 3px; | |||||
| font-weight: 400; | |||||
| position: relative; | |||||
| left: -5px; | |||||
| & div:first-child { | |||||
| padding-left: 8px; | |||||
| } | |||||
| ` | |||||
| width: 210px; | |||||
| height: 35px; | |||||
| font-family: "Open Sans"; | |||||
| margin-top: 3px; | |||||
| font-weight: 400; | |||||
| position: relative; | |||||
| left: -5px; | |||||
| & div:first-child { | |||||
| padding-left: 8px; | |||||
| } | |||||
| @media (max-width: 650px) { | |||||
| width: 144px; | |||||
| height: 30px; | |||||
| font-size: 14px; | |||||
| } | |||||
| `; | |||||
| export const SelectItem = styled(MenuItem)` | export const SelectItem = styled(MenuItem)` | ||||
| font-family: "Open Sans"; | |||||
| ` | |||||
| font-family: "Open Sans"; | |||||
| `; | |||||
| export const SelectOption = styled(Option)` | |||||
| @media (max-width: 600px) { | |||||
| height: 20px !important; | |||||
| min-height: 35px; | |||||
| margin: 2px; | |||||
| } | |||||
| `; | |||||
| export const IconStyled = styled(Box)` | export const IconStyled = styled(Box)` | ||||
| position: relative; | |||||
| top: 0; | |||||
| right: 10px; | |||||
| ` | |||||
| position: relative; | |||||
| top: 0; | |||||
| right: 10px; | |||||
| `; | |||||
| export const HeaderButtons = styled(Box)` | export const HeaderButtons = styled(Box)` | ||||
| flex-direction: row; | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| margin-right: 40px; | |||||
| ` | |||||
| flex-direction: row; | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| margin-right: 40px; | |||||
| `; | |||||
| export const HeaderAltLocation = styled(Typography)` | |||||
| font-family: "Open Sans"; | |||||
| font-size: 16px; | |||||
| color: ${selectedTheme.primaryText}; | |||||
| margin-left: 5px; | |||||
| @media (max-width: 600px) { | |||||
| display: none; | |||||
| } | |||||
| ` |
| export const MarketPlaceContainer = styled(Box)` | export const MarketPlaceContainer = styled(Box)` | ||||
| height: 100%; | height: 100%; | ||||
| margin: 0 70px; | margin: 0 70px; | ||||
| @media (max-width: 600px) { | |||||
| margin: 0 1.8rem; | |||||
| @media (max-width: 550px) { | |||||
| margin: -30px 1.8rem; | |||||
| } | } | ||||
| `; | `; |
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { OffersContainer } from "./Offers.styled"; | import { OffersContainer } from "./Offers.styled"; | ||||
| import OfferCard from "../../Cards/OfferCard/OfferCard"; | import OfferCard from "../../Cards/OfferCard/OfferCard"; | ||||
| import { | |||||
| fetchMoreOffers, | |||||
| fetchOffers, | |||||
| } from "../../../store/actions/offers/offersActions"; | |||||
| import { fetchOffers } from "../../../store/actions/offers/offersActions"; | |||||
| import { useDispatch, useSelector } from "react-redux"; | import { useDispatch, useSelector } from "react-redux"; | ||||
| import { | import { | ||||
| selectNoMoreOffers, | |||||
| selectOffers, | selectOffers, | ||||
| selectPinnedOffers, | selectPinnedOffers, | ||||
| selectTotalOffers, | |||||
| } from "../../../store/selectors/offersSelectors"; | } from "../../../store/selectors/offersSelectors"; | ||||
| import useFilters from "../../../hooks/useFilters"; | |||||
| // import useFilters from "../../../hooks/useFilters"; | |||||
| import Paging from "../../Paging/Paging"; | |||||
| // import { convertQueryString } from "../../../util/helpers/queryHelpers"; | |||||
| import { HOME_PAGE } from "../../../constants/pages"; | |||||
| import { useHistory } from "react-router-dom"; | |||||
| // import qs from "query-string"; | |||||
| // import useSorting from "../../../hooks/useSorting"; | |||||
| import { useQueryString } from "../../../hooks/useQueryString"; | |||||
| const Offers = (props) => { | const Offers = (props) => { | ||||
| const filters = useFilters(); | |||||
| const [page, setPage] = useState(2); | |||||
| const [initialLoad, setInitialLoad] = useState(true); | |||||
| // const filters = useFilters(); | |||||
| const [page, setPage] = useState(1); | |||||
| // const [initialLoad, setInitialLoad] = useState(true); | |||||
| const pinnedOffers = useSelector(selectPinnedOffers); | const pinnedOffers = useSelector(selectPinnedOffers); | ||||
| const offers = useSelector(selectOffers); | const offers = useSelector(selectOffers); | ||||
| const total = useSelector(selectTotalOffers); | |||||
| const history = useHistory(); | |||||
| // const sorting = useSorting(); | |||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const offersRef = useRef(null); | const offersRef = useRef(null); | ||||
| const noMoreOffersStatus = useSelector(selectNoMoreOffers); | |||||
| let timeout = null; | |||||
| let listener; | |||||
| const queryStringHook = useQueryString(); | |||||
| // const queryString = history.location.search.substring(1); | |||||
| // const queryObject = qs.parse(queryString); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| listener = () => { | |||||
| if ( | |||||
| !noMoreOffersStatus && | |||||
| offers?.length > 0 && | |||||
| window.scrollY + window.innerHeight > | |||||
| window.document.body.offsetHeight * 0.8 | |||||
| ) { | |||||
| if (!timeout) { | |||||
| timeout = setTimeout(() => { | |||||
| timeout = null; | |||||
| }, 5000); | |||||
| dispatch( | |||||
| fetchMoreOffers({ page: page, queryString: filters.queryString }) | |||||
| ); | |||||
| } | |||||
| } | |||||
| }; | |||||
| window.addEventListener("scroll", listener); | |||||
| return () => window.removeEventListener("scroll", listener); | |||||
| }, [page, noMoreOffersStatus, filters.queryString, timeout, offers]); | |||||
| useEffect(() => { | |||||
| setPage(Math.floor((offers.length + pinnedOffers.length ) / 10) + 1); | |||||
| }, [offers, pinnedOffers]); | |||||
| let queryObject = queryStringHook.getQueryObject(); | |||||
| if (queryObject.page && queryObject.page !== 1) { | |||||
| setPage(parseInt(queryObject.page)); | |||||
| } | |||||
| }, [history.location.search]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| dispatch(fetchOffers({ page: 1 })); | |||||
| }, []); | |||||
| // console.log("sorting.loadedQS", sorting.loadedQS); | |||||
| // console.log("queryString", queryString) | |||||
| if (queryStringHook.loadedFromURL) { | |||||
| dispatch(fetchOffers({ queryString: "?" + queryStringHook.queryString })); | |||||
| history.push({ | |||||
| pathname: HOME_PAGE, | |||||
| search: queryStringHook.getGlobalQueryString(), | |||||
| }); | |||||
| window.scrollTo({ | |||||
| top: 0, | |||||
| behavior: "smooth", | |||||
| }); | |||||
| const queryObject = new URLSearchParams(queryStringHook.queryString); | |||||
| if (queryObject.has("page")) { | |||||
| if (queryObject.get("page") !== page.toString()) | |||||
| setPage(parseInt(queryObject.get("page"))); | |||||
| } else { | |||||
| setPage(1); | |||||
| } | |||||
| } else { | |||||
| queryStringHook.appendMultipleToQueryString([ | |||||
| { key: "size", value: "10" }, | |||||
| { key: "page", value: "1" }, | |||||
| ]); | |||||
| // queryStringHook.appendToQueryString("page", 1); | |||||
| } | |||||
| }, [queryStringHook.loadedFromURL, queryStringHook.queryString]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (filters.queryString) { | |||||
| if (filters.queryString.length > 1) { | |||||
| if (initialLoad) { | |||||
| dispatch(fetchOffers({ page: 1, queryString: filters.queryString })); | |||||
| setInitialLoad(false); | |||||
| // if (page !== 1) { | |||||
| const queryObject = new URLSearchParams(queryStringHook.queryString); | |||||
| if (queryObject.has("page")) { | |||||
| if (queryObject.get("page") !== page.toString()) { | |||||
| queryStringHook.appendToQueryString("page", page); | |||||
| } | } | ||||
| } else { | } else { | ||||
| setInitialLoad(false); | |||||
| queryStringHook.appendToQueryString("page", page); | |||||
| } | } | ||||
| } | |||||
| }, [filters.queryString, initialLoad]); | |||||
| console.log("PAGE KONZOLAAAAAAAAAAAAAAAAAAAAAA") | |||||
| // } | |||||
| }, [page]); | |||||
| // useEffect(() => { | |||||
| // if (filters.queryString) { | |||||
| // if (filters.queryString.length > 1) { | |||||
| // if (initialLoad) { | |||||
| // dispatch(fetchOffers({ page: 1, queryString: filters.queryString })); | |||||
| // setInitialLoad(false); | |||||
| // } | |||||
| // } else { | |||||
| // setInitialLoad(false); | |||||
| // } | |||||
| // } | |||||
| // }, [filters.queryString, initialLoad]); | |||||
| const handleDifferentPage = (pageNum) => { | |||||
| setPage(pageNum); | |||||
| }; | |||||
| return ( | return ( | ||||
| <OffersContainer ref={offersRef}> | <OffersContainer ref={offersRef}> | ||||
| <OfferCard key={item._id} offer={item} halfwidth={props.isGrid} /> | <OfferCard key={item._id} offer={item} halfwidth={props.isGrid} /> | ||||
| ); | ); | ||||
| })} | })} | ||||
| <Paging | |||||
| totalElements={total} | |||||
| elementsPerPage={10} | |||||
| current={page} | |||||
| changePage={handleDifferentPage} | |||||
| /> | |||||
| </OffersContainer> | </OffersContainer> | ||||
| ); | ); | ||||
| }; | }; |
| flex-direction: row; | flex-direction: row; | ||||
| flex-wrap: wrap; | flex-wrap: wrap; | ||||
| justify-content: space-between; | justify-content: space-between; | ||||
| margin-top: 5px; | |||||
| position: relative; | |||||
| padding-bottom: 60px; | |||||
| `; | `; |
| import React from "react"; | |||||
| import PropTypes from "prop-types"; | |||||
| import { | |||||
| Arrow, | |||||
| ArrowIcon, | |||||
| PageNumber, | |||||
| PagingContainer, | |||||
| ThreeDots, | |||||
| } from "./Paging.styled"; | |||||
| const Paging = (props) => { | |||||
| const pages = props.pages | |||||
| ? props.pages | |||||
| : props.totalElements | |||||
| ? Math.ceil(props.totalElements / props.elementsPerPage) | |||||
| : 1; | |||||
| let moving = 0; | |||||
| const pagesAsArray = Array.apply(null, Array(5)).map(() => {}); | |||||
| const threeDotsBefore = props.current - 2 > 1; | |||||
| const threeDotsAfter = props.current + 2 < pages; | |||||
| return ( | |||||
| <PagingContainer> | |||||
| <Arrow | |||||
| onClick={() => props.changePage(props.current - 1)} | |||||
| disabled={props.current - 1 < 1} | |||||
| > | |||||
| <ArrowIcon side="left" /> | |||||
| </Arrow> | |||||
| {threeDotsBefore && ( | |||||
| <React.Fragment> | |||||
| <PageNumber onClick={() => props.changePage(1)}>1</PageNumber> | |||||
| {props.current - 3 !== 1 && <ThreeDots>...</ThreeDots>} | |||||
| </React.Fragment> | |||||
| )} | |||||
| {pagesAsArray.map((item, index) => { | |||||
| const pageNum = props.current - 2 + moving++; | |||||
| if (pageNum > pages ) return; | |||||
| if (pageNum < 1) return; | |||||
| return ( | |||||
| <PageNumber | |||||
| current={pageNum === props.current} | |||||
| key={index} | |||||
| onClick={() => props.changePage(pageNum)} | |||||
| > | |||||
| {pageNum} | |||||
| </PageNumber> | |||||
| ); | |||||
| })} | |||||
| {threeDotsAfter && ( | |||||
| <React.Fragment> | |||||
| {props.current + 3 !== pages && <ThreeDots>...</ThreeDots>} | |||||
| <PageNumber onClick={() => props.changePage(pages)}> | |||||
| {pages} | |||||
| </PageNumber> | |||||
| </React.Fragment> | |||||
| )} | |||||
| <Arrow | |||||
| onClick={() => props.changePage(props.current + 1)} | |||||
| disabled={props.current + 1 > pages} | |||||
| > | |||||
| <ArrowIcon side="right" /> | |||||
| </Arrow> | |||||
| </PagingContainer> | |||||
| ); | |||||
| }; | |||||
| Paging.propTypes = { | |||||
| children: PropTypes.any, | |||||
| totalElements: PropTypes.number, | |||||
| elementsPerPage: PropTypes.number, | |||||
| pages: PropTypes.number, | |||||
| current: PropTypes.number, | |||||
| changePage: PropTypes.func, | |||||
| }; | |||||
| export default Paging; |
| import { Box, Button } from "@mui/material"; | |||||
| import styled from "styled-components"; | |||||
| import selectedTheme from "../../themes"; | |||||
| import { ReactComponent as DownArrow } from "../../assets/images/svg/arrow-down.svg"; | |||||
| export const PagingContainer = styled(Box)` | |||||
| width: calc(100% / 12 * 8.5); | |||||
| text-align: center; | |||||
| font-family: "Open Sans"; | |||||
| display: flex; | |||||
| flex: 1; | |||||
| justify-content: center; | |||||
| flex-direction: row; | |||||
| margin-top: 5px; | |||||
| margin-bottom: 10px; | |||||
| position: absolute; | |||||
| bottom: 0; | |||||
| padding-left: 0; | |||||
| padding-right: 0; | |||||
| margin: auto; | |||||
| left: 0; | |||||
| right: 0; | |||||
| `; | |||||
| export const ArrowIcon = styled(DownArrow)` | |||||
| ${(props) => | |||||
| props.side === "left" && | |||||
| ` | |||||
| transform: rotate(180deg); | |||||
| `} | |||||
| width: 18px; | |||||
| height: 18px; | |||||
| & path { | |||||
| ${(props) => | |||||
| props.disabled && | |||||
| ` | |||||
| stroke: ${selectedTheme.iconStrokeDisabledColor} | |||||
| `} | |||||
| } | |||||
| `; | |||||
| export const Arrow = styled(Button)` | |||||
| border: 1px solid ${selectedTheme.primaryPurple}; | |||||
| border-radius: 100%; | |||||
| min-width: 36px; | |||||
| width: 36px; | |||||
| height: 36px; | |||||
| display: block; | |||||
| box-sizing: border-box; | |||||
| cursor: pointer; | |||||
| padding-left: 8px; | |||||
| padding-top: 8px; | |||||
| margin: auto 10px; | |||||
| transition: 0.2s all ease; | |||||
| &:hover { | |||||
| background-color: ${selectedTheme.primaryPurple}; | |||||
| & svg path { | |||||
| stroke: white; | |||||
| } | |||||
| } | |||||
| ${(props) => | |||||
| props.disabled && | |||||
| ` | |||||
| border 1px solid ${selectedTheme.iconStrokeDisabledColor}; | |||||
| & svg path { | |||||
| stroke: ${selectedTheme.iconStrokeDisabledColor}; | |||||
| transition: 0.2s all ease; | |||||
| } | |||||
| `} | |||||
| @media (max-width: 600px) { | |||||
| width: 30px; | |||||
| min-width: 30px; | |||||
| height: 30px; | |||||
| padding-top: 5px; | |||||
| padding-left: 5px; | |||||
| } | |||||
| `; | |||||
| export const PageNumber = styled(Box)` | |||||
| color: ${(props) => (!props.current ? selectedTheme.primaryPurple : "white")}; | |||||
| font-weight: 600; | |||||
| font-size: 16px; | |||||
| line-height: 18px; | |||||
| font-family: "Open Sans"; | |||||
| height: 40px; | |||||
| min-width: 40px; | |||||
| max-width: 40px; | |||||
| width: 40px !important; | |||||
| margin: 5px; | |||||
| padding-top: 10px; | |||||
| background-color: ${(props) => props.current && selectedTheme.primaryPurple}; | |||||
| border-radius: 100%; | |||||
| position: relative; | |||||
| top: 1px; | |||||
| &:hover { | |||||
| cursor: pointer; | |||||
| ${(props) => | |||||
| !props.current && | |||||
| ` | |||||
| color: ${props.current ? selectedTheme.primaryPurple : "white"}; | |||||
| background-color: ${!props.current && selectedTheme.primaryPurple}; | |||||
| `} | |||||
| } | |||||
| @media (max-width: 600px) { | |||||
| height: 30px; | |||||
| min-width: 30px; | |||||
| max-width: 30px; | |||||
| width: 30px !important; | |||||
| padding-top: 6px; | |||||
| font-size: 14px; | |||||
| margin: 1px; | |||||
| } | |||||
| `; | |||||
| export const ThreeDots = styled(Box)` | |||||
| color: ${(props) => (!props.current ? selectedTheme.primaryPurple : "white")}; | |||||
| font-weight: 600; | |||||
| font-size: 16px; | |||||
| line-height: 18px; | |||||
| font-family: "Open Sans"; | |||||
| height: 40px; | |||||
| min-width: 40px; | |||||
| max-width: 40px; | |||||
| width: 40px !important; | |||||
| margin: 5px; | |||||
| padding-top: 10px; | |||||
| background-color: ${(props) => props.current && selectedTheme.primaryPurple}; | |||||
| border-radius: 100%; | |||||
| position: relative; | |||||
| top: 1px; | |||||
| @media (max-width: 600px) { | |||||
| height: 10px; | |||||
| min-width: 10px; | |||||
| max-width: 10px; | |||||
| width: 10px !important; | |||||
| padding-top: 6px; | |||||
| font-size: 14px; | |||||
| margin: 1px; | |||||
| } | |||||
| ` |
| import React from "react"; | import React from "react"; | ||||
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { | import { | ||||
| EyeIcon, | |||||
| HeaderPopoverContainer, | HeaderPopoverContainer, | ||||
| PopoverButton, | PopoverButton, | ||||
| PopoverButtonsContainer, | |||||
| PopoverList, | PopoverList, | ||||
| PopoverListItem, | PopoverListItem, | ||||
| PopoverListItemAvatar, | PopoverListItemAvatar, | ||||
| PopoverListItemAvatarContainer, | PopoverListItemAvatarContainer, | ||||
| PopoverListItemProfileAvatar, | PopoverListItemProfileAvatar, | ||||
| PopoverListItemTextContainer, | PopoverListItemTextContainer, | ||||
| PopoverNoItemsText, | |||||
| PopoverTitle, | PopoverTitle, | ||||
| } from "./HeaderPopover.styled"; | } from "./HeaderPopover.styled"; | ||||
| import selectedTheme from "../../../themes"; | |||||
| const HeaderPopover = (props) => { | const HeaderPopover = (props) => { | ||||
| return ( | return ( | ||||
| <HeaderPopoverContainer> | <HeaderPopoverContainer> | ||||
| <PopoverTitle p={2}>{props.title}</PopoverTitle> | <PopoverTitle p={2}>{props.title}</PopoverTitle> | ||||
| <PopoverList> | <PopoverList> | ||||
| {props.items.map((item, index) => ( | |||||
| {props.items?.length > 0 ? props.items.map((item, index) => ( | |||||
| <PopoverListItem key={index}> | <PopoverListItem key={index}> | ||||
| <PopoverListItemAvatarContainer> | <PopoverListItemAvatarContainer> | ||||
| {props.isProfile ? ( | |||||
| <PopoverListItemProfileAvatar alt={item.alt} src={item.src} /> | |||||
| ) : ( | |||||
| <PopoverListItemAvatar alt={item.alt} src={item.src} /> | |||||
| )} | |||||
| {props.isProfile ? ( | |||||
| <PopoverListItemProfileAvatar alt={item.alt} src={item.src} /> | |||||
| ) : ( | |||||
| <PopoverListItemAvatar alt={item.alt} src={item.src} /> | |||||
| )} | |||||
| </PopoverListItemAvatarContainer> | </PopoverListItemAvatarContainer> | ||||
| <PopoverListItemTextContainer | <PopoverListItemTextContainer | ||||
| primary={item.title} | |||||
| secondary={item.text} | |||||
| > | |||||
| </PopoverListItemTextContainer> | |||||
| primary={item.title} | |||||
| secondary={item.text} | |||||
| ></PopoverListItemTextContainer> | |||||
| </PopoverListItem> | </PopoverListItem> | ||||
| ))} | |||||
| )) : ( | |||||
| <PopoverNoItemsText>No items at the moment...</PopoverNoItemsText> | |||||
| )} | |||||
| </PopoverList> | </PopoverList> | ||||
| <PopoverButton | |||||
| sx={{ | |||||
| <PopoverButtonsContainer> | |||||
| <PopoverButton | |||||
| sx={{ | |||||
| mr: 2, | mr: 2, | ||||
| mb: 2, | mb: 2, | ||||
| }} | |||||
| variant="text" | |||||
| endIcon={<EyeIcon color={selectedTheme.iconYellowColor} />} | |||||
| > | |||||
| {props.buttonText} | |||||
| </PopoverButton> | |||||
| }} | |||||
| variant="text" | |||||
| endIcon={props.buttonIcon} | |||||
| onClick={props.buttonOnClick} | |||||
| > | |||||
| {props.buttonText} | |||||
| </PopoverButton> | |||||
| {props.secondButtonText && ( | |||||
| <PopoverButton | |||||
| sx={{ | |||||
| mr: 2, | |||||
| mb: 2, | |||||
| }} | |||||
| variant="text" | |||||
| endIcon={props.secondButtonIcon} | |||||
| onClick={props.secondButtonOnClick} | |||||
| > | |||||
| {props.secondButtonText} | |||||
| </PopoverButton> | |||||
| )} | |||||
| </PopoverButtonsContainer> | |||||
| </HeaderPopoverContainer> | </HeaderPopoverContainer> | ||||
| ); | ); | ||||
| }; | }; | ||||
| items: PropTypes.array, | items: PropTypes.array, | ||||
| buttonText: PropTypes.string, | buttonText: PropTypes.string, | ||||
| isProfile: PropTypes.bool, | isProfile: PropTypes.bool, | ||||
| secondButtonText: PropTypes.string, | |||||
| buttonIcon: PropTypes.any, | |||||
| secondButtonIcon: PropTypes.any, | |||||
| buttonOnClick: PropTypes.func, | |||||
| secondButtonOnClick: PropTypes.func, | |||||
| }; | }; | ||||
| export default HeaderPopover; | export default HeaderPopover; |
| ` | ` | ||||
| export const PopoverButton = styled(Button)` | export const PopoverButton = styled(Button)` | ||||
| text-decoration: underline; | text-decoration: underline; | ||||
| float: right; | |||||
| color: ${selectedTheme.primaryPurple}; | color: ${selectedTheme.primaryPurple}; | ||||
| font-weight: 500; | font-weight: 500; | ||||
| text-align: right; | |||||
| height: 20px; | |||||
| ` | ` | ||||
| export const PopoverListItemTextContainer = styled(ListItemText)` | export const PopoverListItemTextContainer = styled(ListItemText)` | ||||
| & span { | & span { | ||||
| & path { | & path { | ||||
| stroke: ${selectedTheme.primaryYellow}; | stroke: ${selectedTheme.primaryYellow}; | ||||
| } | } | ||||
| ` | |||||
| export const PopoverButtonsContainer = styled(Box)` | |||||
| flex-direction: column; | |||||
| display: flex; | |||||
| align-items: flex-end; | |||||
| ` | |||||
| export const PopoverNoItemsText = styled(Typography)` | |||||
| text-align: center; | |||||
| width: 100%; | |||||
| font-weight: 600; | |||||
| padding-top: 5px; | |||||
| font-size: 13px; | |||||
| font-family: "Open Sans"; | |||||
| ` | ` |
| import React, { useEffect } from "react"; | |||||
| import React, { useEffect, useState } from "react"; | |||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import { useDispatch, useSelector } from "react-redux"; | import { useDispatch, useSelector } from "react-redux"; | ||||
| import { fetchChats } from "../../../store/actions/chat/chatActions"; | |||||
| import { fetchHeaderChats } from "../../../store/actions/chat/chatActions"; | |||||
| import { selectLatestChats } from "../../../store/selectors/chatSelectors"; | import { selectLatestChats } from "../../../store/selectors/chatSelectors"; | ||||
| import { selectUserId } from "../../../store/selectors/loginSelectors"; | import { selectUserId } from "../../../store/selectors/loginSelectors"; | ||||
| import HeaderPopover from "../HeaderPopover/HeaderPopover"; | import HeaderPopover from "../HeaderPopover/HeaderPopover"; | ||||
| const dummyData1 = [ | |||||
| { | |||||
| alt: "Remy Sharp", | |||||
| src: "/static/images/avatar/1.jpg", | |||||
| title: "Coca-Cola", | |||||
| text: "Kompresor je stigao. Samo...", | |||||
| }, | |||||
| { | |||||
| alt: "Travis Howard", | |||||
| src: "/static/images/avatar/2.jpg", | |||||
| title: "Voda Vrnjci", | |||||
| text: "Poslao sam vodu. Ukupno i...", | |||||
| }, | |||||
| ]; | |||||
| const convertMessages = (messages) => { | const convertMessages = (messages) => { | ||||
| const allMessages = [ | |||||
| ...messages.chatsFromMyOffers, | |||||
| ...messages.initiatedChats, | |||||
| ]; | |||||
| const lastMessageDate = Math.max( | |||||
| ...allMessages.map((item) => new Date(item._modified)) | |||||
| ); | |||||
| const lastMessage = allMessages.find( | |||||
| (item) => | |||||
| JSON.stringify(new Date(item._modified)) === | |||||
| JSON.stringify(new Date(lastMessageDate)) | |||||
| ); | |||||
| const lastSecondMessageDate = Math.max([...allMessages.filter(item => item._id !== lastMessage._id).map(item => new Date(item._modified))]) | |||||
| console.log(lastSecondMessageDate); | |||||
| console.log(allMessages.filter(item => item._id !== lastMessage._id).map(item => new Date(item._modified))) | |||||
| console.log(lastMessage); | |||||
| return messages.map((item) => ({ | |||||
| alt: "Tekst", | |||||
| src: item.interlocutorData.image, | |||||
| title: item.interlocutorData.name, | |||||
| text: item?.chat?.messages[0]?.text, | |||||
| })); | |||||
| }; | }; | ||||
| export const MyMessages = () => { | export const MyMessages = () => { | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const userId = useSelector(selectUserId); | const userId = useSelector(selectUserId); | ||||
| const chats = useSelector(selectLatestChats); | const chats = useSelector(selectLatestChats); | ||||
| convertMessages(chats); | |||||
| const [lastChats, setLastChats] = useState([]); | |||||
| useEffect(() => { | |||||
| if (userId?.length > 1) { | |||||
| dispatch(fetchHeaderChats(userId)); | |||||
| } | |||||
| }, [userId]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| dispatch(fetchChats(userId)); | |||||
| }, []); | |||||
| if (chats?.length > 0) { | |||||
| setLastChats([...convertMessages(chats)]); | |||||
| } | |||||
| }, [chats]); | |||||
| return ( | return ( | ||||
| <HeaderPopover | <HeaderPopover | ||||
| title={t("header.myMessages")} | title={t("header.myMessages")} | ||||
| items={dummyData1} | |||||
| items={lastChats} | |||||
| buttonText={t("header.checkEverything")} | buttonText={t("header.checkEverything")} | ||||
| /> | /> | ||||
| ); | ); |
| import React from "react"; | |||||
| import React, { useEffect, useState } from "react"; | |||||
| import { PostsImgSuit } from "./MyPosts.styled"; | import { PostsImgSuit } from "./MyPosts.styled"; | ||||
| const dummyData2 = [ | |||||
| { | |||||
| alt: "Remy Sharp", | |||||
| src: "/static/images/avatar/1.jpg", | |||||
| title: "Gitara", | |||||
| text: (<React.Fragment><PostsImgSuit/> Player.rs</React.Fragment>) | |||||
| }, | |||||
| { | |||||
| alt: "Remy Sharp", | |||||
| src: "/static/images/avatar/1.jpg", | |||||
| title: "Gitara", | |||||
| text: (<React.Fragment><PostsImgSuit/> Player.rs</React.Fragment>) | |||||
| } | |||||
| ] | |||||
| // const dummyData2 = [ | |||||
| // { | |||||
| // alt: "Remy Sharp", | |||||
| // src: "/static/images/avatar/1.jpg", | |||||
| // title: "Gitara", | |||||
| // text: (<React.Fragment><PostsImgSuit/> Player.rs</React.Fragment>) | |||||
| // }, | |||||
| // { | |||||
| // alt: "Remy Sharp", | |||||
| // src: "/static/images/avatar/1.jpg", | |||||
| // title: "Gitara", | |||||
| // text: (<React.Fragment><PostsImgSuit/> Player.rs</React.Fragment>) | |||||
| // } | |||||
| // ] | |||||
| 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 { selectMineOffers } from "../../../store/selectors/offersSelectors"; | |||||
| import { fetchMineOffers } from "../../../store/actions/offers/offersActions"; | |||||
| import { selectProfileName } from "../../../store/selectors/profileSelectors"; | |||||
| export const MyPosts = () => { | export const MyPosts = () => { | ||||
| const {t} = useTranslation(); | |||||
| const { t } = useTranslation(); | |||||
| const dispatch = useDispatch(); | |||||
| const mineOffers = useSelector(selectMineOffers); | |||||
| const name = useSelector(selectProfileName); | |||||
| const [arrayOfMineOffers, setArrayOfMineOffers] = useState([]); | |||||
| useEffect(() => { | |||||
| dispatch(fetchMineOffers()); | |||||
| }, []); | |||||
| useEffect(() => { | |||||
| if (mineOffers?.length > 0) { | |||||
| if (mineOffers.length > 1) { | |||||
| setArrayOfMineOffers( | |||||
| [mineOffers[0], mineOffers[1]].map((item) => ({ | |||||
| alt: "Photo", | |||||
| src: item.images[0], | |||||
| title: item.name, | |||||
| text: ( | |||||
| <React.Fragment> | |||||
| <PostsImgSuit /> {name} | |||||
| </React.Fragment> | |||||
| ), | |||||
| })) | |||||
| ); | |||||
| } else if (mineOffers.length > 0) { | |||||
| setArrayOfMineOffers( | |||||
| [mineOffers[0]].map((item) => ({ | |||||
| alt: "Photo", | |||||
| src: item.images[0], | |||||
| title: item.name, | |||||
| text: ( | |||||
| <React.Fragment> | |||||
| <PostsImgSuit /> {name} | |||||
| </React.Fragment> | |||||
| ), | |||||
| })) | |||||
| ); | |||||
| } else { | |||||
| setArrayOfMineOffers([]) | |||||
| } | |||||
| } | |||||
| }); | |||||
| return ( | return ( | ||||
| <HeaderPopover | <HeaderPopover | ||||
| title={t("header.myOffers")} | |||||
| items={dummyData2} | |||||
| buttonText={t("header.checkEverything")}/> | |||||
| title={t("header.myOffers")} | |||||
| items={arrayOfMineOffers} | |||||
| buttonText={t("header.checkEverything")} | |||||
| /> | |||||
| ); | ); | ||||
| }; | }; |
| import React from "react"; | |||||
| import { ProfileImgPIB } from "./MyProfile.styled"; | |||||
| import React, { useEffect, useState } from "react"; | |||||
| import { LogoutIcon, ProfileImgPIB } from "./MyProfile.styled"; | |||||
| import HeaderPopover from "../HeaderPopover/HeaderPopover"; | import HeaderPopover from "../HeaderPopover/HeaderPopover"; | ||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| const dummyData3 = [ | |||||
| { | |||||
| alt: "Profile", | |||||
| src: "/static/images/avatar/2.jpg", | |||||
| title: "Player.rs", | |||||
| text: <React.Fragment><ProfileImgPIB/> PIB - 1234567890 </React.Fragment> | |||||
| }, | |||||
| ]; | |||||
| import { useDispatch, useSelector } from "react-redux"; | |||||
| import { selectProfile } from "../../../store/selectors/profileSelectors"; | |||||
| import { selectUserId } from "../../../store/selectors/loginSelectors"; | |||||
| import { fetchProfile } from "../../../store/actions/profile/profileActions"; | |||||
| import selectedTheme from "../../../themes"; | |||||
| import { EyeIcon } from "../HeaderPopover/HeaderPopover.styled"; | |||||
| import { logoutUser } from "../../../store/actions/login/loginActions"; | |||||
| import { useHistory } from "react-router-dom"; | |||||
| import { LOGIN_PAGE } from "../../../constants/pages"; | |||||
| export const MyProfile = () => { | export const MyProfile = () => { | ||||
| const {t} = useTranslation(); | |||||
| const { t } = useTranslation(); | |||||
| const profile = useSelector(selectProfile); | |||||
| const userId = useSelector(selectUserId); | |||||
| const dispatch = useDispatch(); | |||||
| const history = useHistory(); | |||||
| const [profileAsArray, setProfileAsArray] = useState([]); | |||||
| useEffect(() => { | |||||
| if (userId?.length > 1) { | |||||
| dispatch(fetchProfile(userId)); | |||||
| } | |||||
| }, [userId]); | |||||
| useEffect(() => { | |||||
| if (profile?.statistics) { | |||||
| setProfileAsArray([ | |||||
| { | |||||
| alt: "Profile", | |||||
| src: `${profile.image}`, | |||||
| title: profile.company.name, | |||||
| text: ( | |||||
| <React.Fragment> | |||||
| <ProfileImgPIB /> | |||||
| PIB - {profile.company.PIB} | |||||
| </React.Fragment> | |||||
| ), | |||||
| }, | |||||
| ]); | |||||
| } | |||||
| }, [profile]); | |||||
| const handleLogoutSuccess = () => { | |||||
| history.replace(LOGIN_PAGE); | |||||
| }; | |||||
| const handleLogout = () => { | |||||
| dispatch(logoutUser(handleLogoutSuccess)); | |||||
| }; | |||||
| return ( | return ( | ||||
| <HeaderPopover | <HeaderPopover | ||||
| title={t("header.myProfile")} | title={t("header.myProfile")} | ||||
| items={dummyData3} | |||||
| items={profileAsArray} | |||||
| buttonText={t("header.checkProfile")} | buttonText={t("header.checkProfile")} | ||||
| buttonIcon={<EyeIcon color={selectedTheme.iconYellowColor} />} | |||||
| isProfile | isProfile | ||||
| secondButtonIcon={<LogoutIcon color={selectedTheme.iconYellowColor} />} | |||||
| secondButtonText={"Odjavite se"} | |||||
| secondButtonOnClick={handleLogout} | |||||
| /> | /> | ||||
| ); | ); | ||||
| }; | }; |
| import { TextField, Avatar } from "@mui/material"; | import { TextField, Avatar } from "@mui/material"; | ||||
| import styled from "styled-components"; | import styled from "styled-components"; | ||||
| import PIB from '@mui/icons-material/AdminPanelSettingsOutlined'; | import PIB from '@mui/icons-material/AdminPanelSettingsOutlined'; | ||||
| import {ReactComponent as Logout} from "../../../assets/images/svg/log-out.svg"; | |||||
| export const ProfileImgPIB = styled(PIB)` | export const ProfileImgPIB = styled(PIB)` | ||||
| width: 1rem; | width: 1rem; | ||||
| @media (max-width: 600px) { | @media (max-width: 600px) { | ||||
| width: 36%; | width: 36%; | ||||
| } | } | ||||
| ` | |||||
| export const LogoutIcon = styled(Logout)` | |||||
| ` | ` |
| import { Box, Select } from "@mui/material"; | import { Box, Select } from "@mui/material"; | ||||
| import styled from "styled-components"; | import styled from "styled-components"; | ||||
| import selectedTheme from "../../themes"; | |||||
| export const SelectStyled = styled(Select)` | export const SelectStyled = styled(Select)` | ||||
| width: ${props => props.width}; | width: ${props => props.width}; | ||||
| font-weight: 600; | font-weight: 600; | ||||
| font-family: "Open Sans"; | font-family: "Open Sans"; | ||||
| cursor: pointer; | cursor: pointer; | ||||
| & fieldset { | |||||
| border-color: ${props => props.borderColor ? props.borderColor : selectedTheme.primaryPurple} !important; | |||||
| } | |||||
| ` | ` | ||||
| export const SelectIcon = styled(Box)` | export const SelectIcon = styled(Box)` | ||||
| position: relative; | position: relative; |
| setIsFieldEmpty(false); | setIsFieldEmpty(false); | ||||
| } | } | ||||
| }, [props.value]); | }, [props.value]); | ||||
| return ( | return ( | ||||
| <TextFieldContainer | <TextFieldContainer | ||||
| style={props.containerStyle} | style={props.containerStyle} |
| import { useEffect, useState } from "react"; | import { useEffect, useState } from "react"; | ||||
| import { useDispatch } from "react-redux"; | import { useDispatch } from "react-redux"; | ||||
| import { useSelector } from "react-redux"; | import { useSelector } from "react-redux"; | ||||
| import { useHistory } from "react-router-dom"; | |||||
| // import { useHistory } from "react-router-dom"; | |||||
| import { fetchCategories } from "../store/actions/categories/categoriesActions"; | import { fetchCategories } from "../store/actions/categories/categoriesActions"; | ||||
| import { | import { | ||||
| setFilteredCategory, | setFilteredCategory, | ||||
| setFilteredLocations, | setFilteredLocations, | ||||
| setFilteredSubcategory, | setFilteredSubcategory, | ||||
| setIsAppliedStatus, | setIsAppliedStatus, | ||||
| setQueryString, | |||||
| // setQueryString, | |||||
| } from "../store/actions/filters/filtersActions"; | } from "../store/actions/filters/filtersActions"; | ||||
| import { fetchLocations } from "../store/actions/locations/locationsActions"; | import { fetchLocations } from "../store/actions/locations/locationsActions"; | ||||
| import { | import { | ||||
| } from "../store/selectors/categoriesSelectors"; | } from "../store/selectors/categoriesSelectors"; | ||||
| import { | import { | ||||
| selectAppliedStatus, | selectAppliedStatus, | ||||
| selectQueryString, | |||||
| // selectQueryString, | |||||
| selectSelectedCategory, | selectSelectedCategory, | ||||
| selectSelectedLocations, | selectSelectedLocations, | ||||
| selectSelectedSubcategory, | selectSelectedSubcategory, | ||||
| } from "../store/selectors/filtersSelectors"; | } from "../store/selectors/filtersSelectors"; | ||||
| import qs from "query-string"; | |||||
| import { selectLocations } from "../store/selectors/locationsSelectors"; | import { selectLocations } from "../store/selectors/locationsSelectors"; | ||||
| import { fetchOffers } from "../store/actions/offers/offersActions"; | |||||
| import { HOME_PAGE } from "../constants/pages"; | |||||
| import { convertQueryString } from "../util/helpers/queryHelpers"; | |||||
| // import { fetchOffers } from "../store/actions/offers/offersActions"; | |||||
| // import { HOME_PAGE } from "../constants/pages"; | |||||
| // import { convertQueryString } from "../util/helpers/queryHelpers"; | |||||
| // import qs from "query-string"; | |||||
| import { useQueryString } from "./useQueryString"; | |||||
| // import qs from "query-string"; | |||||
| const useFilters = () => { | const useFilters = () => { | ||||
| const queryString = useSelector(selectQueryString); | |||||
| const selectedCategory = useSelector(selectSelectedCategory); | const selectedCategory = useSelector(selectSelectedCategory); | ||||
| const selectedSubcategory = useSelector(selectSelectedSubcategory); | const selectedSubcategory = useSelector(selectSelectedSubcategory); | ||||
| const selectedLocations = useSelector(selectSelectedLocations); | const selectedLocations = useSelector(selectSelectedLocations); | ||||
| const [loadedFromQS, setLoadedFromQS] = useState(false); | const [loadedFromQS, setLoadedFromQS] = useState(false); | ||||
| const [loaded, setLoadedStatus] = useState(false); | const [loaded, setLoadedStatus] = useState(false); | ||||
| const isApplied = useSelector(selectAppliedStatus); | const isApplied = useSelector(selectAppliedStatus); | ||||
| const history = useHistory(); | |||||
| // const history = useHistory(); | |||||
| const categories = useSelector(selectCategories); | const categories = useSelector(selectCategories); | ||||
| const subcategories = useSelector( | const subcategories = useSelector( | ||||
| selectSubcategories(selectedCategory?.name) | selectSubcategories(selectedCategory?.name) | ||||
| ); | ); | ||||
| const locations = useSelector(selectLocations); | const locations = useSelector(selectLocations); | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const queryStringHook = useQueryString(); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (!loaded) { | if (!loaded) { | ||||
| dispatch(fetchCategories()); | dispatch(fetchCategories()); | ||||
| setLoadedStatus(true); | setLoadedStatus(true); | ||||
| } | } | ||||
| }, [categories, locations]); | }, [categories, locations]); | ||||
| // useEffect(() => { | |||||
| // if (loadedFromQS) { | |||||
| // makeQueryString(); | |||||
| // } | |||||
| // }, [selectedCategory, selectedLocations, selectedSubcategory, loadedFromQS]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (!loadedFromQS) { | |||||
| if (categories && locations) { | |||||
| const queryString = history.location.search.substring(1); | |||||
| const queryObject = qs.parse(queryString); | |||||
| let category; | |||||
| if (queryObject.category) { | |||||
| category = categories.find( | |||||
| (item) => item.name === queryObject.category.toString() | |||||
| ); | |||||
| setSelectedCategory(category); | |||||
| } | |||||
| if (queryObject.subcategory) { | |||||
| setSelectedSubcategory( | |||||
| category.subcategories.find( | |||||
| (item) => | |||||
| item.name.toString() === queryObject.subcategory.toString() | |||||
| ) | |||||
| ); | |||||
| } | |||||
| if (queryObject.location) { | |||||
| let locationsToPush = []; | |||||
| if (Array.isArray(queryObject.location)) { | |||||
| queryObject.location.forEach((item) => { | |||||
| locationsToPush.push(locations.find((p) => p.city === item)); | |||||
| }); | |||||
| } else { | |||||
| const queryObject = new URLSearchParams(queryStringHook.queryString); | |||||
| if (categories?.length > 0 && locations?.length > 0) { | |||||
| let category; | |||||
| if (queryObject.has("category")) { | |||||
| category = categories.find( | |||||
| (item) => item.name === queryObject.get("category").toString() | |||||
| ); | |||||
| setSelectedCategory(category); | |||||
| } | |||||
| if (queryObject.has("subcategory")) { | |||||
| setSelectedSubcategory( | |||||
| category?.subcategories?.find( | |||||
| (item) => | |||||
| item.name.toString() === queryObject.get("subcategory").toString() | |||||
| ) | |||||
| ); | |||||
| } | |||||
| if (queryObject.has("location")) { | |||||
| let locationsToPush = []; | |||||
| // if (Array.isArray(queryObject.location)) { | |||||
| queryObject.getAll("location").forEach((item) => { | |||||
| locationsToPush.push( | locationsToPush.push( | ||||
| locations.find((p) => p.city === queryObject.location) | |||||
| locations.find((p) => p.city === item) | |||||
| ); | ); | ||||
| } | |||||
| setSelectedLocations([...locationsToPush]); | |||||
| } | |||||
| // For future changes if needed | |||||
| // if (queryObject.sortBy) { | |||||
| // if (queryObject.sortBy === sortOptions.OLD.queryString) { | |||||
| // setSelectedSortOption(sortOptions.OLD); | |||||
| // } | |||||
| // if (queryObject.sortBy === sortOptions.NEW.queryString) { | |||||
| // setSelectedSortOption(sortOptions.NEW); | |||||
| // } | |||||
| // if (queryObject.sortBy === sortOptions.POPULAR.queryString) { | |||||
| // setSelectedSortOption(sortOptions.POPULAR); | |||||
| // } | |||||
| }); | |||||
| // } else { | |||||
| // locationsToPush.push( | |||||
| // locations.find((p) => p.city === queryObject.location) | |||||
| // ); | |||||
| // } | // } | ||||
| setLoadedFromQS(true); | |||||
| dispatch(setIsAppliedStatus(true)); | |||||
| setSelectedLocations([...locationsToPush]); | |||||
| } | } | ||||
| } | } | ||||
| }, [history.location.search]); | |||||
| useEffect(() => { | |||||
| if (loadedFromQS) { | |||||
| makeQueryString(); | |||||
| } | |||||
| }, [ | |||||
| selectedCategory, | |||||
| selectedLocations, | |||||
| selectedSubcategory, | |||||
| loadedFromQS, | |||||
| ]); | |||||
| }, [queryStringHook.queryString, categories, locations]); | |||||
| // useEffect(() => { | |||||
| // console.log("if (!loadedFromQS) {"); | |||||
| // console.log("if (!loadedFromQS) {", queryString); | |||||
| // if (!loadedFromQS) { | |||||
| // if (categories && locations) { | |||||
| // const queryString = history.location.search.substring(1); | |||||
| // const queryObject = qs.parse(queryString); | |||||
| // console.log(queryObject) | |||||
| // let category; | |||||
| // if (queryObject.category) { | |||||
| // category = categories.find( | |||||
| // (item) => item.name === queryObject.category.toString() | |||||
| // ); | |||||
| // setSelectedCategory(category); | |||||
| // } | |||||
| // if (queryObject.subcategory) { | |||||
| // setSelectedSubcategory( | |||||
| // category?.subcategories?.find( | |||||
| // (item) => | |||||
| // item.name.toString() === queryObject.subcategory.toString() | |||||
| // ) | |||||
| // ); | |||||
| // } | |||||
| // if (queryObject.location) { | |||||
| // let locationsToPush = []; | |||||
| // if (Array.isArray(queryObject.location)) { | |||||
| // queryObject.location.forEach((item) => { | |||||
| // locationsToPush.push( | |||||
| // locations.find((p) => p.city === item) | |||||
| // ); | |||||
| // }); | |||||
| // } else { | |||||
| // locationsToPush.push( | |||||
| // locations.find((p) => p.city === queryObject.location) | |||||
| // ); | |||||
| // } | |||||
| // setSelectedLocations([...locationsToPush]); | |||||
| // } | |||||
| // setLoadedFromQS(true); | |||||
| // dispatch(setIsAppliedStatus(true)); | |||||
| // } | |||||
| // } | |||||
| // }, [history.location.search]); | |||||
| // Apply everything | // Apply everything | ||||
| const applyFilters = () => { | const applyFilters = () => { | ||||
| dispatch(setIsAppliedStatus(true)); | |||||
| dispatch(fetchOffers({ queryString: queryString })); | |||||
| history.push({ | |||||
| pathname: HOME_PAGE, | |||||
| search: convertQueryString(queryString), | |||||
| }); | |||||
| window.scrollTo({ | |||||
| top: 0, | |||||
| behavior: "smooth", | |||||
| }); | |||||
| // console.log("dispatch(setIsAppliedStatus(true));"); | |||||
| // console.log("dispatch(setIsAppliedStatus(true));", queryString); | |||||
| // dispatch(setIsAppliedStatus(true)); | |||||
| // dispatch(fetchOffers({ queryString: queryStringHook.getQueryString() })); | |||||
| // history.push({ | |||||
| // pathname: HOME_PAGE, | |||||
| // search: queryStringHook.getGlobalQueryString(), | |||||
| // }); | |||||
| // window.scrollTo({ | |||||
| // top: 0, | |||||
| // behavior: "smooth", | |||||
| // }); | |||||
| makeQueryString(); | |||||
| }; | }; | ||||
| // Clear function | // Clear function | ||||
| // Helper function | // Helper function | ||||
| const makeQueryString = () => { | const makeQueryString = () => { | ||||
| let qsArray = []; | let qsArray = []; | ||||
| // if (selectedCategory && selectedCategory?._id !== 0) { | |||||
| // qsArray.push("category=" + encodeURIComponent(selectedCategory.name)); | |||||
| // queryStringHook.appendToQueryString("category", selectedCategory.name); | |||||
| qsArray.push({key: "category", value: selectedCategory?.name }) | |||||
| // } | |||||
| // if (selectedSubcategory && selectedSubcategory?._id !== 0) { | |||||
| // qsArray.push( | |||||
| // "subcategory=" + encodeURIComponent(selectedSubcategory.name) | |||||
| // ); | |||||
| // queryStringHook.appendToQueryString("subcategory", selectedSubcategory.name) | |||||
| qsArray.push({key: "subcategory", value: selectedSubcategory?.name}) | |||||
| // } | |||||
| // if (selectedLocations && selectedLocations?.length > 0) { | |||||
| selectedLocations?.forEach((location) => { | |||||
| // qsArray.push("location=" + encodeURIComponent(location.city)); | |||||
| // queryStringHook.appendToQueryString("location", location.city); | |||||
| qsArray.push({key: "location", value: location?.city}) | |||||
| }); | |||||
| qsArray.push({key: "page", value: "1"}) | |||||
| // } | |||||
| // let qsStringFromArray = "?" + qsArray.join("&"); | |||||
| // dispatch(setQueryString(qsStringFromArray)); | |||||
| queryStringHook.appendMultipleToQueryString(qsArray); | |||||
| }; | |||||
| //Calculate chosen categories for number above filter icon on mobile responsive version | |||||
| const calculateFiltersChosen = () => { | |||||
| let sum = 0; | |||||
| if (selectedCategory && selectedCategory?._id !== 0) { | if (selectedCategory && selectedCategory?._id !== 0) { | ||||
| qsArray.push("category=" + encodeURIComponent(selectedCategory.name)); | |||||
| sum++; | |||||
| } | } | ||||
| if (selectedSubcategory && selectedSubcategory?._id !== 0) { | if (selectedSubcategory && selectedSubcategory?._id !== 0) { | ||||
| qsArray.push( | |||||
| "subcategory=" + encodeURIComponent(selectedSubcategory.name) | |||||
| ); | |||||
| sum++; | |||||
| } | } | ||||
| if (selectedLocations && selectedLocations?.length > 0) { | if (selectedLocations && selectedLocations?.length > 0) { | ||||
| selectedLocations.forEach((location) => { | |||||
| qsArray.push("location=" + encodeURIComponent(location.city)); | |||||
| }); | |||||
| sum += selectedLocations.length; | |||||
| } | } | ||||
| let qsStringFromArray = "?" + qsArray.join("&"); | |||||
| // if (queryString.length > 1) { | |||||
| // dispatch(setQueryString(queryString + "&" + qsStringFromArray)); | |||||
| // } else { | |||||
| // let queryStringEdited = new URLSearchParams(qsStringFromArray); | |||||
| dispatch(setQueryString(qsStringFromArray)); | |||||
| // } | |||||
| return sum; | |||||
| }; | }; | ||||
| // Setters | // Setters | ||||
| const setSelectedCategory = (payload) => { | const setSelectedCategory = (payload) => { | ||||
| if (isApplied !== false) { | if (isApplied !== false) { | ||||
| dispatch(setIsAppliedStatus(false)); | dispatch(setIsAppliedStatus(false)); | ||||
| } | } | ||||
| dispatch(setFilteredCategory(payload)); | |||||
| if (JSON.stringify(payload) !== JSON.stringify(selectedCategory)) { | |||||
| dispatch(setFilteredCategory(payload)); | |||||
| } | |||||
| }; | }; | ||||
| const setSelectedSubcategory = (payload) => { | const setSelectedSubcategory = (payload) => { | ||||
| if (isApplied !== false) { | if (isApplied !== false) { | ||||
| dispatch(setIsAppliedStatus(false)); | dispatch(setIsAppliedStatus(false)); | ||||
| } | } | ||||
| dispatch(setFilteredSubcategory(payload)); | |||||
| if (JSON.stringify(payload) !== JSON.stringify(selectedSubcategory)) { | |||||
| dispatch(setFilteredSubcategory(payload)); | |||||
| } | |||||
| }; | }; | ||||
| const setSelectedLocations = (payload) => { | const setSelectedLocations = (payload) => { | ||||
| if (isApplied !== false) { | if (isApplied !== false) { | ||||
| dispatch(setIsAppliedStatus(false)); | dispatch(setIsAppliedStatus(false)); | ||||
| } | } | ||||
| dispatch(setFilteredLocations(payload)); | |||||
| if (JSON.stringify(payload) !== JSON.stringify(selectedLocations)) { | |||||
| dispatch(setFilteredLocations(payload)); | |||||
| } | |||||
| }; | }; | ||||
| return { | return { | ||||
| selectedCategory, | selectedCategory, | ||||
| categories, | categories, | ||||
| subcategories, | subcategories, | ||||
| locations, | locations, | ||||
| queryString, | |||||
| applyFilters, | applyFilters, | ||||
| clearFilters, | clearFilters, | ||||
| isApplied, | isApplied, | ||||
| makeQueryString | |||||
| makeQueryString, | |||||
| calculateFiltersChosen, | |||||
| loadedFromQS, | |||||
| setLoadedFromQS, | |||||
| }; | }; | ||||
| }; | }; | ||||
| import { useState } from "react" | |||||
| export const usePaging = () => { | |||||
| const [page, setPage] = useState(); | |||||
| return { | |||||
| page, setPage | |||||
| } | |||||
| } |
| import _ from "lodash"; | |||||
| 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"; | |||||
| // import useSorting from "./useSorting"; | |||||
| // import { sortEnum } from "../enums/sortEnum"; | |||||
| import { convertQueryStringBackend, convertQueryStringFrontend } from "../util/helpers/queryHelpers"; | |||||
| export const useQueryString = () => { | |||||
| const queryString = useSelector(selectQueryString); | |||||
| const history = useHistory(); | |||||
| const [globalQueryString, setGlobalQueryString] = useState(""); | |||||
| const [initial, setInitial] = useState(true); | |||||
| const [loadedFromURL, setLoadedFromURL] = useState(false); | |||||
| // const [isFetched, setIsFetched] = useState(false); | |||||
| const dispatch = useDispatch(); | |||||
| // const sorting = useSorting(); | |||||
| useEffect(() => { | |||||
| const queryStringLocal = history.location.search.substring(1); | |||||
| setQueryString(convertQueryStringBackend(queryStringLocal)); | |||||
| setGlobalQueryString(queryStringLocal); | |||||
| console.log("initial bre") | |||||
| }, []); | |||||
| useEffect(() => { | |||||
| console.log(globalQueryString); | |||||
| console.log(history.location.search.substring(1)) | |||||
| if (globalQueryString === history.location.search.substring(1)) | |||||
| setLoadedFromURL(true); | |||||
| // qo.delete("location") | |||||
| }, [globalQueryString]); | |||||
| useEffect(() => { | |||||
| if (initial && loadedFromURL) { | |||||
| if (queryString?.length > 0) { | |||||
| const fun = _.once(() => { | |||||
| setGlobalQueryString(convertQueryStringFrontend(queryString)); | |||||
| setInitial(false); | |||||
| }); | |||||
| fun(); | |||||
| } | |||||
| } else { | |||||
| setGlobalQueryString(convertQueryStringFrontend(queryString)); | |||||
| } | |||||
| }, [queryString, loadedFromURL]); | |||||
| useEffect(() => { | |||||
| if (!initial) { | |||||
| history.push({ | |||||
| pathname: HOME_PAGE, | |||||
| search: "?" + globalQueryString, | |||||
| }); | |||||
| } | |||||
| }, [globalQueryString, initial]); | |||||
| const getQueryString = () => { | |||||
| return queryString; | |||||
| }; | |||||
| const setQueryString = (newQueryString) => { | |||||
| dispatch(setQueryStringSaga(newQueryString)); | |||||
| }; | |||||
| const getQueryObject = () => { | |||||
| const urlParams = new URLSearchParams(queryString); | |||||
| return Object.fromEntries(urlParams); | |||||
| }; | |||||
| const appendToQueryString = (key, value) => { | |||||
| if (loadedFromURL) { | |||||
| let urlParams = new URLSearchParams(queryString); | |||||
| if (key === "location") { | |||||
| if (urlParams.has(key)) { | |||||
| let arrayOfLocations = urlParams.getAll(key); | |||||
| if (arrayOfLocations.includes(value)) { | |||||
| arrayOfLocations = arrayOfLocations.filter( | |||||
| (item) => item?.toString() !== value?.toString() | |||||
| ); | |||||
| urlParams.delete(key); | |||||
| arrayOfLocations.forEach((item) => { | |||||
| urlParams.append(key, item); | |||||
| }); | |||||
| } | |||||
| } | |||||
| } else { | |||||
| if (urlParams.has(key)) { | |||||
| urlParams.delete(key); | |||||
| } | |||||
| } | |||||
| if (!value) setQueryString(urlParams.toString()); | |||||
| urlParams.append(key, value); | |||||
| setQueryString(urlParams.toString()); | |||||
| return urlParams.toString(); | |||||
| } | |||||
| }; | |||||
| const appendMultipleToQueryString = (array = []) => { | |||||
| if (loadedFromURL) { | |||||
| let urlParams = new URLSearchParams(queryString); | |||||
| if ( | |||||
| array.find((item) => item.key === "category") || | |||||
| array.find((item) => item.key === "subcategory") | |||||
| ) { | |||||
| urlParams.delete("location"); | |||||
| } | |||||
| array.forEach((item) => { | |||||
| // if (item.key === "location") { | |||||
| // if (urlParams.has(item.key)) { | |||||
| // let arrayOfLocations = urlParams.getAll(item.key); | |||||
| // if (arrayOfLocations.includes(item.value)) { | |||||
| // arrayOfLocations = arrayOfLocations.filter( | |||||
| // (itemInList) => | |||||
| // itemInList?.toString() !== item?.value?.toString() | |||||
| // ); | |||||
| // urlParams.delete(item.key); | |||||
| // arrayOfLocations.forEach((location) => { | |||||
| // urlParams.append(item.key, location); | |||||
| // }); | |||||
| // } | |||||
| // } | |||||
| // } else { | |||||
| if (urlParams.has(item.key) && item.key !== "location") { | |||||
| urlParams.delete(item.key); | |||||
| } | |||||
| // } | |||||
| console.log("item.key: ", item.key); | |||||
| console.log("item.value: ", item.value); | |||||
| if (!item.value) return; | |||||
| urlParams.append(item.key, item.value); | |||||
| console.log("querytString: ", urlParams.toString()) | |||||
| }); | |||||
| console.log("postavlja se"); | |||||
| setQueryString(urlParams.toString()); | |||||
| return urlParams.toString(); | |||||
| } | |||||
| }; | |||||
| const deleteFromQueryString = (key, value = null) => { | |||||
| let urlParams = new URLSearchParams(queryString); | |||||
| if (key === "location") { | |||||
| let arrayOfLocations = urlParams.getAll(key); | |||||
| arrayOfLocations = arrayOfLocations.filter((item) => item !== value); | |||||
| urlParams.delete(key); | |||||
| arrayOfLocations.forEach((item) => { | |||||
| urlParams.append(key, item); | |||||
| }); | |||||
| } else if (key === "sortBy") { | |||||
| urlParams.delete("_des_date"); | |||||
| urlParams.delete("_des_popular"); | |||||
| } else { | |||||
| urlParams.delete(key); | |||||
| } | |||||
| setQueryString(urlParams.toString()); | |||||
| return urlParams.toString(); | |||||
| }; | |||||
| const getInitialQueryString = () => { | |||||
| let urlParams = new URLSearchParams(queryString); | |||||
| urlParams = new URLSearchParams(appendToQueryString("size", 10)); | |||||
| urlParams = new URLSearchParams(appendToQueryString("page", 1)); | |||||
| return urlParams; | |||||
| }; | |||||
| const getGlobalQueryString = () => { | |||||
| return globalQueryString; | |||||
| }; | |||||
| return { | |||||
| queryString, | |||||
| globalQueryString, | |||||
| getQueryString, | |||||
| setQueryString, | |||||
| getQueryObject, | |||||
| initial, | |||||
| loadedFromURL, | |||||
| appendMultipleToQueryString, | |||||
| getGlobalQueryString, | |||||
| appendToQueryString, | |||||
| getInitialQueryString, | |||||
| deleteFromQueryString, | |||||
| }; | |||||
| }; |
| import { useEffect, useState } from "react"; | |||||
| const getScreenDimensions = () => { | |||||
| const height = window.innerHeight; | |||||
| const width = window.innerWidth; | |||||
| return { | |||||
| height, | |||||
| width | |||||
| } | |||||
| } | |||||
| const useScreenDimensions = () => { | |||||
| const [screenDimensions, setScreenDimensions] = useState(getScreenDimensions()); | |||||
| const [width, setWidth] = useState(getScreenDimensions().width); | |||||
| const [height, setHeight] = useState(getScreenDimensions().height); | |||||
| useEffect(() => { | |||||
| const resize = () => { | |||||
| setScreenDimensions(getScreenDimensions()); | |||||
| setWidth(getScreenDimensions().width); | |||||
| setHeight(getScreenDimensions().height); | |||||
| } | |||||
| window.addEventListener('resize', resize); | |||||
| resize(); | |||||
| return () => window.removeEventListener('resize', resize); | |||||
| }, []) | |||||
| return { | |||||
| screenDimensions, width, height | |||||
| } | |||||
| } | |||||
| export default useScreenDimensions; |
| import { useDispatch, useSelector } from "react-redux"; | |||||
| import { fetchOffers } from "../store/actions/offers/offersActions"; | |||||
| import { selectQueryString } from "../store/selectors/filtersSelectors"; | |||||
| // import { useDispatch } from "react-redux"; | |||||
| import { useQueryString } from "./useQueryString"; | |||||
| // import { fetchOffers } from "../store/actions/offers/offersActions"; | |||||
| // import { selectQueryString } from "../store/selectors/queryStringSelectors"; | |||||
| // import { selectQueryString } from "../store/selectors/filtersSelectors"; | |||||
| export const useSearch = () => { | export const useSearch = () => { | ||||
| const dispatch = useDispatch(); | |||||
| const queryString = useSelector(selectQueryString); | |||||
| // const dispatch = useDispatch(); | |||||
| const queryStringHook = useQueryString(); | |||||
| // const queryString = useSelector(selectQueryString); | |||||
| const searchOffers = (searchString) => { | const searchOffers = (searchString) => { | ||||
| const queryObject = new URLSearchParams(queryString); | |||||
| queryObject.set('oname', searchString); | |||||
| dispatch(fetchOffers({queryString: "?" + queryObject.toString()})); | |||||
| queryStringHook.appendToQueryString("oname", searchString); | |||||
| // const queryObject = new URLSearchParams(queryString); | |||||
| // queryObject.set('oname', searchString); | |||||
| // dispatch(fetchOffers({queryString: "?" + queryObject.toString()})); | |||||
| } | } | ||||
| return { | return { |
| import { useEffect, useState } from "react"; | |||||
| // import { useEffect, useState } from "react"; | |||||
| import { useEffect } from "react"; | |||||
| import { useDispatch, useSelector } from "react-redux"; | import { useDispatch, useSelector } from "react-redux"; | ||||
| import { useHistory } from "react-router-dom"; | |||||
| import { HOME_PAGE } from "../constants/pages"; | |||||
| // import { useHistory } from "react-router-dom"; | |||||
| // import { HOME_PAGE } from "../constants/pages"; | |||||
| import { sortEnum } from "../enums/sortEnum"; | import { sortEnum } from "../enums/sortEnum"; | ||||
| import { setFilteredSortOption} from "../store/actions/filters/filtersActions"; | |||||
| import { fetchOffers } from "../store/actions/offers/offersActions"; | |||||
| import qs from "query-string"; | |||||
| import { | |||||
| selectQueryString, | |||||
| selectSelectedSortOption, | |||||
| } from "../store/selectors/filtersSelectors"; | |||||
| import useFilters from "./useFilters"; | |||||
| import { setFilteredSortOption } from "../store/actions/filters/filtersActions"; | |||||
| // import { fetchOffers } from "../store/actions/offers/offersActions"; | |||||
| // import qs from "query-string"; | |||||
| import { selectSelectedSortOption } from "../store/selectors/filtersSelectors"; | |||||
| // import useFilters from "./useFilters"; | |||||
| // import { convertQueryString } from "../util/helpers/queryHelpers"; | |||||
| import { useQueryString } from "./useQueryString"; | |||||
| import { convertQueryStringFrontend } from "../util/helpers/queryHelpers"; | |||||
| const useSorting = () => { | const useSorting = () => { | ||||
| // Local query string is query string used for fetching data from API | |||||
| const [localQueryString, setLocalQueryString] = useState(""); | |||||
| // Global query string is query string shown for user (more user-friendly) | |||||
| const [globalQueryString, setGlobalQueryString] = useState(""); | |||||
| const history = useHistory(); | |||||
| const filters = useFilters(); | |||||
| const queryString = useSelector(selectQueryString); | |||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const selectedSortOption = useSelector(selectSelectedSortOption); | const selectedSortOption = useSelector(selectSelectedSortOption); | ||||
| const sortOptions = sortEnum; | const sortOptions = sortEnum; | ||||
| const queryStringHook = useQueryString(); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (localQueryString.length > 0) { | |||||
| if (queryString.length > 1) { | |||||
| let queryStringEdited = new URLSearchParams(queryString); | |||||
| queryStringEdited.delete('_des_date'); | |||||
| queryStringEdited.delete('_des_popular'); | |||||
| dispatch( | |||||
| fetchOffers({ queryString: "?" + queryStringEdited + "&" + localQueryString }) | |||||
| ); | |||||
| history.push({ | |||||
| pathname: HOME_PAGE, | |||||
| search: "?" + queryStringEdited + "&" + globalQueryString, | |||||
| }); | |||||
| } else { | |||||
| dispatch(fetchOffers({queryString: "?" + localQueryString})); | |||||
| history.push({ | |||||
| pathname: HOME_PAGE, | |||||
| search: "?" + globalQueryString, | |||||
| }); | |||||
| } | |||||
| window.scrollTo({ | |||||
| top: 0, | |||||
| behavior: "smooth", | |||||
| }); | |||||
| } | |||||
| }, [localQueryString]); | |||||
| useEffect(() => { | |||||
| const queryStringFromUrl = history.location.search.substring(1); | |||||
| const queryObjectFromUrl = qs.parse(queryStringFromUrl); | |||||
| if (queryObjectFromUrl?.sortBy) { | |||||
| if (queryObjectFromUrl.sortBy === sortOptions.OLD.queryString) { | |||||
| setSelectedSortOption(sortOptions.OLD) | |||||
| } | |||||
| if (queryObjectFromUrl.sortBy === sortOptions.NEW.queryString) { | |||||
| setSelectedSortOption(sortOptions.NEW) | |||||
| } | |||||
| if (queryObjectFromUrl.sortBy === sortOptions.POPULAR.queryString) { | |||||
| setSelectedSortOption(sortOptions.POPULAR) | |||||
| if (queryStringHook.loadedFromURL) { | |||||
| const queryString = queryStringHook.queryString; | |||||
| let queryObject = new URLSearchParams( | |||||
| convertQueryStringFrontend(queryString) | |||||
| ); | |||||
| if (queryObject.has("sortBy")) | |||||
| if (queryObject.get("sortBy") === "newest") { | |||||
| setSelectedSortOption(sortEnum.NEW); | |||||
| } | } | ||||
| } else { | |||||
| setSelectedSortOption(sortOptions.INITIAL) | |||||
| if (queryObject.get("sortBy") === "oldest") { | |||||
| setSelectedSortOption(sortEnum.OLD); | |||||
| } | |||||
| if (queryObject.get("sortBy") === "popular") { | |||||
| setSelectedSortOption(sortEnum.POPULAR); | |||||
| } | |||||
| } | } | ||||
| setGlobalQueryString(qs.stringify(queryObjectFromUrl)); | |||||
| filters.makeQueryString(); | |||||
| }, [history.location.search]); | |||||
| }, [queryStringHook.queryString, queryStringHook.loadedFromURL]); | |||||
| const setSelectedSortOption = (payload) => { | const setSelectedSortOption = (payload) => { | ||||
| dispatch(setFilteredSortOption(payload)); | dispatch(setFilteredSortOption(payload)); | ||||
| console.log("konzola sort: ", payload) | |||||
| let _des_date = null; | let _des_date = null; | ||||
| let _des_popular = null; | let _des_popular = null; | ||||
| if (payload === sortOptions.NEW) { | |||||
| setGlobalQueryString(`sortBy=${sortOptions.NEW.queryString}`); | |||||
| if (payload.value === sortOptions.NEW.value) { | |||||
| _des_date = true; | _des_date = true; | ||||
| } | } | ||||
| if (payload === sortOptions.OLD) { | |||||
| setGlobalQueryString(`sortBy=${sortOptions.OLD.queryString}`); | |||||
| if (payload.value === sortOptions.OLD.value) { | |||||
| _des_date = false; | _des_date = false; | ||||
| } | } | ||||
| if (payload === sortOptions.POPULAR) { | |||||
| setGlobalQueryString(`sortBy=${sortOptions.POPULAR.queryString}`); | |||||
| if (payload.value === sortOptions.POPULAR.value) { | |||||
| _des_popular = true; | _des_popular = true; | ||||
| } | } | ||||
| if (_des_date !== null) { | if (_des_date !== null) { | ||||
| setLocalQueryString(`_des_date=${_des_date}`); | |||||
| queryStringHook.appendMultipleToQueryString([ | |||||
| { key: "_des_date", value: `${_des_date}` }, | |||||
| { key: "_des_popular" }, | |||||
| ]); | |||||
| } | } | ||||
| if (_des_popular !== null) { | if (_des_popular !== null) { | ||||
| setLocalQueryString(`_des_popular=${_des_popular}`); | |||||
| queryStringHook.appendMultipleToQueryString([ | |||||
| { key: "_des_popular", value: `${_des_popular}` }, | |||||
| { key: "_des_date" }, | |||||
| ]); | |||||
| } | } | ||||
| }; | }; | ||||
| return { | return { | ||||
| selectedSortOption, | selectedSortOption, | ||||
| setSelectedSortOption, | setSelectedSortOption, | ||||
| sortOptions, | sortOptions, | ||||
| // loadedQS | |||||
| }; | }; | ||||
| }; | }; | ||||
| export default useSorting; | export default useSorting; |
| forgotPasswordEmail: "Email", | forgotPasswordEmail: "Email", | ||||
| useDifferentEmail: "Iskoristite drugačiju lozinku.", | useDifferentEmail: "Iskoristite drugačiju lozinku.", | ||||
| wrongCredentials: "Pogrešan mail ili lozinka!", | wrongCredentials: "Pogrešan mail ili lozinka!", | ||||
| headerTitle: "Ulogujte se" | |||||
| }, | }, | ||||
| password: { | password: { | ||||
| weak: "slaba", | weak: "slaba", | ||||
| }, | }, | ||||
| register: { | register: { | ||||
| title: "Registruj se", | title: "Registruj se", | ||||
| headerTitle: "Registrujte se", | |||||
| descriptionMain: "Trampa sa kolegama na dohvat ruke", | descriptionMain: "Trampa sa kolegama na dohvat ruke", | ||||
| descriptionFirst: | descriptionFirst: | ||||
| "Email i Lozinka biće Vam primarni način da se ulogujete u aplikaciju", | "Email i Lozinka biće Vam primarni način da se ulogujete u aplikaciju", | ||||
| myOffers: "Moje objave", | myOffers: "Moje objave", | ||||
| checkEverything: "POGLEDAJ SVE", | checkEverything: "POGLEDAJ SVE", | ||||
| myMessages: "Moje poruke", | myMessages: "Moje poruke", | ||||
| newOffers: "Najnovije ponude" | |||||
| }, | }, | ||||
| }; | }; |
| import MarketPlace from "../../components/MarketPlace/MarketPlace"; | import MarketPlace from "../../components/MarketPlace/MarketPlace"; | ||||
| const HomePage = () => { | const HomePage = () => { | ||||
| // const dispatch = useDispatch(); | |||||
| // const history = useHistory(); | |||||
| // useEffect(() => { | |||||
| // let category = null, subcategory = null, cities = [], _des_date = false, _des_popular = false, page, size; | |||||
| // const queryString = history.location.search.substring(1); | |||||
| // const queryObject = qs.parse(queryString); | |||||
| // if (queryObject.category) { | |||||
| // category = Mockupdata[1].find( | |||||
| // (item) => item.string === queryObject.category.toString() | |||||
| // ).id; | |||||
| // } | |||||
| // if (queryObject.subcategory) { | |||||
| // subcategory = Mockupdata[1].find( | |||||
| // (item) => item.string === queryObject.subcategory.toString() | |||||
| // ).id; | |||||
| // } | |||||
| // if (queryObject.city) { | |||||
| // if (Array.isArray(queryObject.city)) { | |||||
| // queryObject.city.forEach((item) => { | |||||
| // cities.push(Mockupdata[0].find((p) => p.string === item).id); | |||||
| // }); | |||||
| // } else { | |||||
| // cities.push( | |||||
| // Mockupdata[0].find((p) => p.string === queryObject.city).id | |||||
| // ); | |||||
| // } | |||||
| // } | |||||
| // if (queryObject.sortBy) { | |||||
| // if (queryObject.sortBy === "dateAsc") _des_date = false; | |||||
| // if (queryObject.sortBy === "dateDesc") _des_date = true; | |||||
| // if (queryObject.sortBy === "popular") _des_popular = true; | |||||
| // } | |||||
| // if (queryObject.page) { | |||||
| // page = queryObject.page; | |||||
| // } | |||||
| // if (queryObject.size) { | |||||
| // size = queryObject.size; | |||||
| // } | |||||
| // dispatch(setFilters({ category, subcategory, cities, _des_date, _des_popular, page, size })); | |||||
| // }, [history.location.search]); | |||||
| return ( | return ( | ||||
| <HomePageContainer> | <HomePageContainer> | ||||
| <MainLayout leftCard={<FilterCard />} content={<MarketPlace />} /> | <MainLayout leftCard={<FilterCard />} content={<MarketPlace />} /> |
| width: 335px; | width: 335px; | ||||
| padding: 0; | padding: 0; | ||||
| flex: 1; | flex: 1; | ||||
| position: relative; | |||||
| /* position: relative; */ | |||||
| transition: 1s all; | transition: 1s all; | ||||
| ${props => props.currentstep === 3 && `margin-top: 40px`}; | ${props => props.currentstep === 3 && `margin-top: 40px`}; | ||||
| @media (max-height: 900px) { | @media (max-height: 900px) { | ||||
| padding: 0; | padding: 0; | ||||
| `; | `; | ||||
| export const Footer = styled(Box)` | export const Footer = styled(Box)` | ||||
| position: absolute; | |||||
| position: relative; | |||||
| bottom: 36px; | bottom: 36px; | ||||
| display: flex; | display: flex; | ||||
| width: 100%; | width: 100%; |
| getOffers: 'offers', | getOffers: 'offers', | ||||
| addOffer: 'offers', | addOffer: 'offers', | ||||
| categories: 'categories', | categories: 'categories', | ||||
| locations: 'locations' | |||||
| locations: 'locations', | |||||
| mineOffers: 'users' | |||||
| } | } | ||||
| }; | }; |
| export const attemptFetchChats = (payload) => { | export const attemptFetchChats = (payload) => { | ||||
| return getRequest(`users/${payload}/chat`); | return getRequest(`users/${payload}/chat`); | ||||
| } | |||||
| export const attemptFetchHeaderChats = (payload) => { | |||||
| return getRequest(`users/${payload}/chat/?page=1&size=2`) | |||||
| } | } |
| import apiEndpoints from "./apiEndpoints" | import apiEndpoints from "./apiEndpoints" | ||||
| export const attemptFetchOffers = (payload) => { | export const attemptFetchOffers = (payload) => { | ||||
| if (payload) return getRequest(apiEndpoints.offers.getOffers + payload + "&size=10&page=1") | |||||
| return getRequest(apiEndpoints.offers.getOffers + "?size=10&page=1") | |||||
| if (payload) return getRequest(apiEndpoints.offers.getOffers + payload) | |||||
| return getRequest(apiEndpoints.offers.getOffers) | |||||
| } | } | ||||
| export const attemptFetchMoreOffers = (page, payload) => { | export const attemptFetchMoreOffers = (page, payload) => { | ||||
| if (payload) return getRequest(apiEndpoints.offers.getOffers + payload + `&size=10&page=${page}`); | if (payload) return getRequest(apiEndpoints.offers.getOffers + payload + `&size=10&page=${page}`); | ||||
| } | } | ||||
| export const attemptAddOffer = (payload) => { | export const attemptAddOffer = (payload) => { | ||||
| return postRequest(apiEndpoints.offers.addOffer, payload) | return postRequest(apiEndpoints.offers.addOffer, payload) | ||||
| } | |||||
| export const attemptFetchMineOffers = (payload) => { | |||||
| return getRequest(`${apiEndpoints.offers.mineOffers}/${payload}/offers`) | |||||
| } | } |
| import { createFetchType } from "../actionHelpers"; | import { createFetchType } from "../actionHelpers"; | ||||
| const CHAT_SCOPE = "CHAT_SCOPE"; | const CHAT_SCOPE = "CHAT_SCOPE"; | ||||
| const CHAT_HEADER_SCOPE = "CHAT_HEADER_SCOPE"; | |||||
| export const CHAT_FETCH = createFetchType(CHAT_SCOPE); | export const CHAT_FETCH = createFetchType(CHAT_SCOPE); | ||||
| export const CHAT_HEADER_FETCH = createFetchType(CHAT_HEADER_SCOPE); | |||||
| export const CHAT_SET = "CHAT_SET"; | export const CHAT_SET = "CHAT_SET"; |
| import { CHAT_FETCH, CHAT_SET } from "./chatActionConstants"; | |||||
| import { CHAT_FETCH, CHAT_HEADER_FETCH, CHAT_SET } from "./chatActionConstants"; | |||||
| export const fetchChats = (payload) => ({ | export const fetchChats = (payload) => ({ | ||||
| type: CHAT_FETCH, | type: CHAT_FETCH, | ||||
| payload, | payload, | ||||
| }) | }) | ||||
| export const fetchHeaderChats = (payload) => ({ | |||||
| type: CHAT_HEADER_FETCH, | |||||
| payload, | |||||
| }) | |||||
| export const setChats = (payload) => ({ | export const setChats = (payload) => ({ | ||||
| type: CHAT_SET, | type: CHAT_SET, | ||||
| payload, | payload, |
| type: AUTHENTICATE_USER, | type: AUTHENTICATE_USER, | ||||
| }); | }); | ||||
| export const logoutUser = () => ({ | |||||
| export const logoutUser = (payload) => ({ | |||||
| type: LOGOUT_USER, | type: LOGOUT_USER, | ||||
| payload, | |||||
| }); | }); | ||||
| export const refreshUserToken = (payload) => ({ | export const refreshUserToken = (payload) => ({ |
| const OFFERS_MORE_SCOPE = "OFFERS_MORE_SCOPE"; | const OFFERS_MORE_SCOPE = "OFFERS_MORE_SCOPE"; | ||||
| export const OFFERS_FETCH_MORE = createFetchType(OFFERS_MORE_SCOPE); | export const OFFERS_FETCH_MORE = createFetchType(OFFERS_MORE_SCOPE); | ||||
| const OFFERS_MINE_SCOPE = "OFFERS_MINE_SCOPE"; | |||||
| export const OFFERS_MINE_FETCH = createFetchType(OFFERS_MINE_SCOPE); | |||||
| export const OFFERS_FETCH = createFetchType(OFFERS_SCOPE); | export const OFFERS_FETCH = createFetchType(OFFERS_SCOPE); | ||||
| export const OFFERS_SUCCESS = createSuccessType(OFFERS_SCOPE); | export const OFFERS_SUCCESS = createSuccessType(OFFERS_SCOPE); | ||||
| export const OFFERS_ERROR = createErrorType(OFFERS_SCOPE); | export const OFFERS_ERROR = createErrorType(OFFERS_SCOPE); | ||||
| export const OFFERS_PINNED_ADD = "OFFERS_PINNED_ADD"; | export const OFFERS_PINNED_ADD = "OFFERS_PINNED_ADD"; | ||||
| export const OFFERS_SET = "OFFERS_SET"; | export const OFFERS_SET = "OFFERS_SET"; | ||||
| export const OFFERS_ADD = "OFFERS_ADD"; | export const OFFERS_ADD = "OFFERS_ADD"; | ||||
| export const OFFERS_NO_MORE = "OFFERS_NO_MORE"; | |||||
| export const OFFERS_NO_MORE = "OFFERS_NO_MORE"; | |||||
| export const OFFERS_SET_TOTAL = "OFFERS_SET_TOTAL"; | |||||
| export const OFFERS_MINE_SET = "OFFERS_MY_ADD"; |
| import { OFFERS_ADD, OFFERS_CLEAR, OFFERS_ERROR, OFFERS_FETCH, OFFERS_FETCH_MORE, OFFERS_NO_MORE, OFFERS_PINNED_ADD, OFFERS_PINNED_SET, OFFERS_SET, OFFERS_SUCCESS} from "./offersActionConstants"; | |||||
| import { | |||||
| OFFERS_ADD, | |||||
| OFFERS_CLEAR, | |||||
| OFFERS_ERROR, | |||||
| OFFERS_FETCH, | |||||
| OFFERS_FETCH_MORE, | |||||
| OFFERS_MINE_FETCH, | |||||
| OFFERS_MINE_SET, | |||||
| OFFERS_NO_MORE, | |||||
| OFFERS_PINNED_ADD, | |||||
| OFFERS_PINNED_SET, | |||||
| OFFERS_SET, | |||||
| OFFERS_SET_TOTAL, | |||||
| OFFERS_SUCCESS, | |||||
| } from "./offersActionConstants"; | |||||
| export const fetchOffers = (payload) => ({ | export const fetchOffers = (payload) => ({ | ||||
| type: OFFERS_FETCH, | |||||
| payload, | |||||
| }) | |||||
| type: OFFERS_FETCH, | |||||
| payload, | |||||
| }); | |||||
| export const fetchOffersSuccess = (payload) => ({ | export const fetchOffersSuccess = (payload) => ({ | ||||
| type: OFFERS_SUCCESS, | |||||
| payload | |||||
| }) | |||||
| type: OFFERS_SUCCESS, | |||||
| payload, | |||||
| }); | |||||
| export const fetchOffersError = (payload) => ({ | export const fetchOffersError = (payload) => ({ | ||||
| type: OFFERS_ERROR, | |||||
| payload, | |||||
| }) | |||||
| type: OFFERS_ERROR, | |||||
| payload, | |||||
| }); | |||||
| export const clearOffers = () => ({ | export const clearOffers = () => ({ | ||||
| type: OFFERS_CLEAR, | |||||
| }) | |||||
| type: OFFERS_CLEAR, | |||||
| }); | |||||
| export const setOffers = (payload) => ({ | export const setOffers = (payload) => ({ | ||||
| type: OFFERS_SET, | |||||
| payload, | |||||
| }) | |||||
| type: OFFERS_SET, | |||||
| payload, | |||||
| }); | |||||
| export const setPinnedOffers = (payload) => ({ | export const setPinnedOffers = (payload) => ({ | ||||
| type: OFFERS_PINNED_SET, | |||||
| payload | |||||
| }) | |||||
| type: OFFERS_PINNED_SET, | |||||
| payload, | |||||
| }); | |||||
| export const addPinnedOffers = (payload) => ({ | export const addPinnedOffers = (payload) => ({ | ||||
| type: OFFERS_PINNED_ADD, | |||||
| payload, | |||||
| }) | |||||
| type: OFFERS_PINNED_ADD, | |||||
| payload, | |||||
| }); | |||||
| export const addOffers = (payload) => ({ | export const addOffers = (payload) => ({ | ||||
| type: OFFERS_ADD, | |||||
| payload | |||||
| }) | |||||
| type: OFFERS_ADD, | |||||
| payload, | |||||
| }); | |||||
| export const fetchMoreOffers = (payload) => ({ | export const fetchMoreOffers = (payload) => ({ | ||||
| type: OFFERS_FETCH_MORE, | |||||
| payload | |||||
| }) | |||||
| type: OFFERS_FETCH_MORE, | |||||
| payload, | |||||
| }); | |||||
| export const setNoMoreOffersStatus = (payload) => ({ | export const setNoMoreOffersStatus = (payload) => ({ | ||||
| type: OFFERS_NO_MORE, | |||||
| payload | |||||
| }) | |||||
| type: OFFERS_NO_MORE, | |||||
| payload, | |||||
| }); | |||||
| export const setTotalOffers = (payload) => ({ | |||||
| type: OFFERS_SET_TOTAL, | |||||
| payload, | |||||
| }); | |||||
| export const fetchMineOffers = () => ({ | |||||
| type: OFFERS_MINE_FETCH, | |||||
| }); | |||||
| export const setMineOffers = (payload) => ({ | |||||
| type: OFFERS_MINE_SET, | |||||
| payload, | |||||
| }); |
| export const QUERY_STRING_SET = "QUERY_STRING_SET"; | |||||
| export const QUERY_STRING_SET_REDUX = "QUERY_STRING_SET_REDUX"; |
| import { QUERY_STRING_SET, QUERY_STRING_SET_REDUX } from "./queryStringActionConstants" | |||||
| export const setQueryStringRedux = (payload) => ({ | |||||
| type: QUERY_STRING_SET_REDUX, | |||||
| payload, | |||||
| }) | |||||
| export const setQueryString = (payload) => ({ | |||||
| type: QUERY_STRING_SET, | |||||
| payload, | |||||
| }) |
| SET_FILTERS, | SET_FILTERS, | ||||
| SET_IS_APPLIED, | SET_IS_APPLIED, | ||||
| SET_LOCATIONS, | SET_LOCATIONS, | ||||
| SET_QUERY_STRING, | |||||
| SET_SORT_OPTION, | SET_SORT_OPTION, | ||||
| SET_SUBCATEGORY, | SET_SUBCATEGORY, | ||||
| } from "../../actions/filters/filtersActionConstants"; | } from "../../actions/filters/filtersActionConstants"; | ||||
| [SET_LOCATIONS]: setFilteredLocations, | [SET_LOCATIONS]: setFilteredLocations, | ||||
| [SET_SORT_OPTION]: setFilteredSortOption, | [SET_SORT_OPTION]: setFilteredSortOption, | ||||
| [SET_IS_APPLIED]: setIsAppliedStatus, | [SET_IS_APPLIED]: setIsAppliedStatus, | ||||
| [SET_QUERY_STRING]: setQueryString, | |||||
| }, | }, | ||||
| initialState | initialState | ||||
| ); | ); | ||||
| isApplied: payload, | isApplied: payload, | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| function setQueryString(state, {payload}) { | |||||
| return { | |||||
| ...state, | |||||
| filters: { | |||||
| ...state.filters, | |||||
| queryString: payload, | |||||
| } | |||||
| } | |||||
| } | } |
| import locationsReducer from "./locations/locationsReducer"; | import locationsReducer from "./locations/locationsReducer"; | ||||
| import profileReducer from "./profile/profileReducer"; | import profileReducer from "./profile/profileReducer"; | ||||
| import chatReducer from "./chat/chatReducer"; | import chatReducer from "./chat/chatReducer"; | ||||
| import queryStringReducer from "./queryString/queryStringReducer"; | |||||
| const loginPersistConfig = { | const loginPersistConfig = { | ||||
| key: "login", | key: "login", | ||||
| categories: persistReducer(categoriesPersistConfig, categoriesReducer), | categories: persistReducer(categoriesPersistConfig, categoriesReducer), | ||||
| locations: persistReducer(locationsPersistConfig, locationsReducer), | locations: persistReducer(locationsPersistConfig, locationsReducer), | ||||
| profile: persistReducer(profilePersistConfig, profileReducer), | profile: persistReducer(profilePersistConfig, profileReducer), | ||||
| chat: persistReducer(chatPersistConfig, chatReducer) | |||||
| chat: persistReducer(chatPersistConfig, chatReducer), | |||||
| queryString: queryStringReducer, | |||||
| }); | }); |
| OFFERS_ADD, | OFFERS_ADD, | ||||
| OFFERS_CLEAR, | OFFERS_CLEAR, | ||||
| OFFERS_ERROR, | OFFERS_ERROR, | ||||
| OFFERS_MINE_SET, | |||||
| OFFERS_NO_MORE, | OFFERS_NO_MORE, | ||||
| OFFERS_PINNED_ADD, | OFFERS_PINNED_ADD, | ||||
| OFFERS_PINNED_SET, | OFFERS_PINNED_SET, | ||||
| OFFERS_SET, | OFFERS_SET, | ||||
| OFFERS_SET_TOTAL, | |||||
| } from "../../actions/offers/offersActionConstants"; | } from "../../actions/offers/offersActionConstants"; | ||||
| import createReducer from "../../utils/createReducer"; | import createReducer from "../../utils/createReducer"; | ||||
| const initialState = { | const initialState = { | ||||
| offers: [], | offers: [], | ||||
| pinnedOffers: [], | pinnedOffers: [], | ||||
| mineOffers: [], | |||||
| total: 0, | |||||
| error: "", | error: "", | ||||
| newOffer: "", | newOffer: "", | ||||
| noMoreOffers: false, | noMoreOffers: false, | ||||
| [OFFERS_NO_MORE]: setNoMoreOffersStatus, | [OFFERS_NO_MORE]: setNoMoreOffersStatus, | ||||
| [OFFERS_PINNED_ADD]: addPinnedOffers, | [OFFERS_PINNED_ADD]: addPinnedOffers, | ||||
| [OFFERS_PINNED_SET]: setPinnedOffers, | [OFFERS_PINNED_SET]: setPinnedOffers, | ||||
| [OFFERS_SET_TOTAL]: setTotalOffers, | |||||
| [OFFERS_MINE_SET]: setMineOffers | |||||
| }, | }, | ||||
| initialState | initialState | ||||
| ); | ); | ||||
| noMoreOffers: action.payload | noMoreOffers: action.payload | ||||
| } | } | ||||
| } | } | ||||
| function setTotalOffers(state, action) { | |||||
| return { | |||||
| ...state, | |||||
| total: action.payload | |||||
| } | |||||
| } | |||||
| function setMineOffers(state, action) { | |||||
| return { | |||||
| ...state, | |||||
| mineOffers: action.payload | |||||
| } | |||||
| } |
| import { QUERY_STRING_SET_REDUX } from "../../actions/queryString/queryStringActionConstants"; | |||||
| import createReducer from "../../utils/createReducer"; | |||||
| const initialState = { | |||||
| queryString: "", | |||||
| }; | |||||
| export default createReducer( | |||||
| { | |||||
| [QUERY_STRING_SET_REDUX]: setQueryStringRedux, | |||||
| }, | |||||
| initialState | |||||
| ); | |||||
| function setQueryStringRedux(state, action) { | |||||
| return { | |||||
| ...state, | |||||
| queryString: action.payload, | |||||
| }; | |||||
| } |
| import { all, takeLatest, call, put } from "@redux-saga/core/effects"; | import { all, takeLatest, call, put } from "@redux-saga/core/effects"; | ||||
| import { attemptFetchChats } from "../../request/chatRequest"; | |||||
| import { CHAT_FETCH } from "../actions/chat/chatActionConstants"; | |||||
| import { attemptFetchChats, attemptFetchHeaderChats } from "../../request/chatRequest"; | |||||
| import { CHAT_FETCH, CHAT_HEADER_FETCH } from "../actions/chat/chatActionConstants"; | |||||
| import { setChats } from "../actions/chat/chatActions"; | import { setChats } from "../actions/chat/chatActions"; | ||||
| function* fetchChats(payload) { | function* fetchChats(payload) { | ||||
| } | } | ||||
| } | } | ||||
| function* fetchHeaderChats(payload) { | |||||
| try { | |||||
| const data = yield call(attemptFetchHeaderChats, payload.payload); | |||||
| yield put(setChats(data.data)); | |||||
| } catch (e) { | |||||
| console.log(e); | |||||
| } | |||||
| } | |||||
| export default function* chatSaga() { | export default function* chatSaga() { | ||||
| yield all([ | yield all([ | ||||
| takeLatest(CHAT_FETCH, fetchChats) | |||||
| takeLatest(CHAT_FETCH, fetchChats), | |||||
| takeLatest(CHAT_HEADER_FETCH, fetchHeaderChats) | |||||
| ]); | ]); | ||||
| } | } |
| import loginSaga from './loginSaga'; | import loginSaga from './loginSaga'; | ||||
| import offersSaga from './offersSaga'; | import offersSaga from './offersSaga'; | ||||
| import profileSaga from './profileSaga'; | import profileSaga from './profileSaga'; | ||||
| import queryStringSaga from './queryStringSaga'; | |||||
| import registerSaga from './registerSaga'; | import registerSaga from './registerSaga'; | ||||
| export default function* rootSaga() { | export default function* rootSaga() { | ||||
| categoriesSaga(), | categoriesSaga(), | ||||
| locationsSaga(), | locationsSaga(), | ||||
| profileSaga(), | profileSaga(), | ||||
| chatSaga() | |||||
| chatSaga(), | |||||
| queryStringSaga(), | |||||
| ]); | ]); | ||||
| } | } |
| } | } | ||||
| } | } | ||||
| function* logout() { | |||||
| function* logout(payload) { | |||||
| try { | try { | ||||
| const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN); | const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN); | ||||
| const user = jwt.decode(JwtToken); | |||||
| const user = yield call(jwt.decode, JwtToken); | |||||
| console.log(payload); | |||||
| if (user) { | if (user) { | ||||
| yield call(logoutUserRequest, { token: JwtToken, userId: user._id }); | |||||
| const requestBody = {token: JwtToken} | |||||
| yield call(logoutUserRequest, requestBody); | |||||
| yield call(payload.payload) | |||||
| } | } | ||||
| } catch (error) { | } catch (error) { | ||||
| console.log(error); // eslint-disable-line | console.log(error); // eslint-disable-line | ||||
| } finally { | } finally { | ||||
| console.log('ovde'); | |||||
| yield call(authScopeClearHelper); | yield call(authScopeClearHelper); | ||||
| yield call(removeHeaderToken); | yield call(removeHeaderToken); | ||||
| yield put(resetLoginState()); | yield put(resetLoginState()); |
| import { all, takeLatest, call, put } from "@redux-saga/core/effects"; | |||||
| import { all, takeLatest, call, put, select } from "@redux-saga/core/effects"; | |||||
| import { | import { | ||||
| attemptFetchMineOffers, | |||||
| attemptFetchMoreOffers, | attemptFetchMoreOffers, | ||||
| attemptFetchOffers, | attemptFetchOffers, | ||||
| } from "../../request/offersRequest"; | } from "../../request/offersRequest"; | ||||
| import { setQueryString } from "../actions/filters/filtersActions"; | |||||
| import { convertQueryStringBackend } from "../../util/helpers/queryHelpers"; | |||||
| // import { setQueryString } from "../actions/filters/filtersActions"; | |||||
| import { | import { | ||||
| OFFERS_FETCH, | OFFERS_FETCH, | ||||
| OFFERS_FETCH_MORE, | OFFERS_FETCH_MORE, | ||||
| OFFERS_MINE_FETCH, | |||||
| } from "../actions/offers/offersActionConstants"; | } from "../actions/offers/offersActionConstants"; | ||||
| import { | import { | ||||
| addOffers, | addOffers, | ||||
| addPinnedOffers, | addPinnedOffers, | ||||
| clearOffers, | clearOffers, | ||||
| setMineOffers, | |||||
| setNoMoreOffersStatus, | setNoMoreOffersStatus, | ||||
| setOffers, | setOffers, | ||||
| setPinnedOffers, | setPinnedOffers, | ||||
| setTotalOffers, | |||||
| } from "../actions/offers/offersActions"; | } from "../actions/offers/offersActions"; | ||||
| import { selectUserId } from "../selectors/loginSelectors"; | |||||
| import { | |||||
| selectOffers, | |||||
| selectPinnedOffers, | |||||
| selectTotalOffers, | |||||
| } from "../selectors/offersSelectors"; | |||||
| function* fetchOffers(payload) { | function* fetchOffers(payload) { | ||||
| try { | try { | ||||
| yield put(clearOffers()); | yield put(clearOffers()); | ||||
| yield put(setNoMoreOffersStatus(false)); | |||||
| const data = yield call(attemptFetchOffers, payload.payload.queryString); | |||||
| if ( | |||||
| data.data.items.pinnedOffers.length + | |||||
| data.data.items.regularOffers.length < | |||||
| 10 || | |||||
| data.data.items.pinnedOffers.length + | |||||
| data.data.items.regularOffers.length > | |||||
| data.data.total | |||||
| ) { | |||||
| yield put(setNoMoreOffersStatus(true)); | |||||
| } | |||||
| if (payload.payload.queryString) { | |||||
| yield put(setQueryString(payload.payload.queryString)); | |||||
| } | |||||
| // adding page and size to query string | |||||
| // let newQueryString = new URLSearchParams(payload.payload.queryString); | |||||
| // if (!newQueryString.has('page')) { | |||||
| // newQueryString.append('page', 1); | |||||
| // if (newQueryString.has('size')) newQueryString.delete('size'); | |||||
| // newQueryString.append('size', 10); | |||||
| // } | |||||
| const newQueryString = new URLSearchParams( | |||||
| convertQueryStringBackend(payload.payload.queryString) | |||||
| ); | |||||
| console.log("fetch offers: ", newQueryString.toString()); | |||||
| const data = yield call( | |||||
| attemptFetchOffers, | |||||
| "?" + newQueryString.toString() | |||||
| ); | |||||
| console.log("if (payload.payload.queryString) {"); | |||||
| // if (payload.payload.queryString) { | |||||
| // yield put(setQueryString(payload.payload.queryString)); | |||||
| // } | |||||
| yield put(setTotalOffers(data.data.total)); | |||||
| yield put(setOffers(data.data.items.regularOffers)); | yield put(setOffers(data.data.items.regularOffers)); | ||||
| yield put(setPinnedOffers(data.data.items.pinnedOffers)); | yield put(setPinnedOffers(data.data.items.pinnedOffers)); | ||||
| } catch (e) { | } catch (e) { | ||||
| payload.payload?.page, | payload.payload?.page, | ||||
| payload.payload?.queryString | payload.payload?.queryString | ||||
| ); | ); | ||||
| if (payload.payload.queryString) { | |||||
| yield put(setQueryString(payload.payload.queryString)); | |||||
| // if (payload.payload.queryString) { | |||||
| // yield put(setQueryString(payload.payload.queryString)); | |||||
| // } | |||||
| const oldOffers = yield select(selectOffers); | |||||
| const oldPinnedOffers = yield select(selectPinnedOffers); | |||||
| const totalNumberOfOffers = yield select(selectTotalOffers); | |||||
| if (oldOffers.length + oldPinnedOffers.length < totalNumberOfOffers) { | |||||
| yield put(addOffers(data.data.items.regularOffers)); | |||||
| yield put(addPinnedOffers(data.data.items.pinnedOffers)); | |||||
| } | } | ||||
| console.log(data.data.items); | |||||
| yield put(addOffers(data.data.items.regularOffers)); | |||||
| yield put(addPinnedOffers(data.data.items.pinnedOffers)); | |||||
| if ( | if ( | ||||
| data.data.items.pinnedOffers + data.data.items.regularOffers < 10 || | |||||
| data.data.items.pinnedOffers.length + | |||||
| data.data.items.regularOffers.length < | |||||
| 10 || | |||||
| data.data.items.pinnedOffers.length + | data.data.items.pinnedOffers.length + | ||||
| data.data.items.regularOffers.length > | data.data.items.regularOffers.length > | ||||
| data.data.total | data.data.total | ||||
| } | } | ||||
| } | } | ||||
| function* fetchMineOffers() { | |||||
| try { | |||||
| const userId = yield select(selectUserId); | |||||
| const data = yield call(attemptFetchMineOffers, userId); | |||||
| yield put(setMineOffers(data.data)); | |||||
| } catch (e) { | |||||
| console.log(e); | |||||
| } | |||||
| } | |||||
| export default function* offersSaga() { | export default function* offersSaga() { | ||||
| yield all([ | yield all([ | ||||
| takeLatest(OFFERS_FETCH, fetchOffers), | takeLatest(OFFERS_FETCH, fetchOffers), | ||||
| takeLatest(OFFERS_FETCH_MORE, fetchMoreOffers), | takeLatest(OFFERS_FETCH_MORE, fetchMoreOffers), | ||||
| takeLatest(OFFERS_MINE_FETCH, fetchMineOffers), | |||||
| ]); | ]); | ||||
| } | } |
| import { all, takeLatest, put } from "@redux-saga/core/effects"; | |||||
| import { convertQueryStringBackend } from "../../util/helpers/queryHelpers"; | |||||
| // import { combineQueryStrings } from "../../util/helpers/queryHelpers"; | |||||
| import { QUERY_STRING_SET } from "../actions/queryString/queryStringActionConstants"; | |||||
| import { setQueryStringRedux } from "../actions/queryString/queryStringActions"; | |||||
| // import { selectQueryString } from "../selectors/queryStringSelectors"; | |||||
| function* setQueryString(payload) { | |||||
| try { | |||||
| console.log("trenutni queryString: ", payload.payload); | |||||
| console.log(payload); | |||||
| // const currentQS = yield select(selectQueryString); | |||||
| // let newQueryString = payload.payload | |||||
| // if (currentQS?.length > 0) { | |||||
| // newQueryString = yield call( | |||||
| // combineQueryStrings, | |||||
| // currentQS, | |||||
| // payload.payload, | |||||
| // ); | |||||
| // } | |||||
| const newQueryString = convertQueryStringBackend(payload.payload) | |||||
| yield put(setQueryStringRedux(newQueryString)); | |||||
| } catch (e) { | |||||
| console.log(e); | |||||
| } | |||||
| } | |||||
| export default function* queryStringSaga() { | |||||
| yield all([takeLatest(QUERY_STRING_SET, setQueryString)]); | |||||
| } |
| filtersSelector, | filtersSelector, | ||||
| (state) => state.filters.isApplied | (state) => state.filters.isApplied | ||||
| ) | ) | ||||
| export const selectQueryString = createSelector( | |||||
| filtersSelector, | |||||
| (state) => state.filters.queryString | |||||
| ) |
| offersSelector, | offersSelector, | ||||
| (state) => state.pinnedOffers | (state) => state.pinnedOffers | ||||
| ) | ) | ||||
| export const selectTotalOffers = createSelector( | |||||
| offersSelector, | |||||
| (state) => state.total | |||||
| ) | |||||
| export const selectMineOffers = createSelector( | |||||
| offersSelector, | |||||
| (state) => state.mineOffers | |||||
| ) |
| import { createSelector } from "reselect"; | |||||
| const queryStringSelector = (state) => state.queryString; | |||||
| export const selectQueryString = createSelector( | |||||
| queryStringSelector, | |||||
| (state => state.queryString) | |||||
| ) |
| export function authScopeStringGetHelper(key) { | export function authScopeStringGetHelper(key) { | ||||
| if (sessionStorage.getItem(SESSION_STORAGE_SCOPE)) { | if (sessionStorage.getItem(SESSION_STORAGE_SCOPE)) { | ||||
| console.log(sessionStorage.getItem(key)) | |||||
| return sessionStorage.getItem(key); | return sessionStorage.getItem(key); | ||||
| } | } | ||||
| console.log(localStorage.getItem(key)) | |||||
| return localStorage.getItem(key); | return localStorage.getItem(key); | ||||
| } | } |
| import { sortEnum } from "../../enums/sortEnum"; | import { sortEnum } from "../../enums/sortEnum"; | ||||
| // import qs from "query-string"; | |||||
| export const convertQueryString = (queryURL) => { | |||||
| const queryObject = new URLSearchParams(queryURL); | |||||
| const queryObjectToReturn = new URLSearchParams(queryURL); | |||||
| if (queryObject.has('_des_date')) { | |||||
| queryObjectToReturn.delete('_des_date'); | |||||
| if (queryObject.get('_des_date') === 'true') { | |||||
| queryObjectToReturn.append('sortBy', sortEnum.NEW.queryString); | |||||
| } else { | |||||
| queryObjectToReturn.append('sortBy', sortEnum.OLD.queryString); | |||||
| } | |||||
| export const convertQueryStringFrontend = (queryURL) => { | |||||
| const queryObject = new URLSearchParams(queryURL); | |||||
| const queryObjectToReturn = new URLSearchParams(queryURL); | |||||
| if (queryObject.has("_des_date")) { | |||||
| queryObjectToReturn.delete("_des_date"); | |||||
| if (queryObject.get("_des_date") === "true") { | |||||
| queryObjectToReturn.append("sortBy", sortEnum.NEW.queryString); | |||||
| } else { | |||||
| queryObjectToReturn.append("sortBy", sortEnum.OLD.queryString); | |||||
| } | } | ||||
| if (queryObject.has('_des_popular')) { | |||||
| queryObjectToReturn.delete('_des_popular'); | |||||
| queryObjectToReturn.append('sortBy', sortEnum.POPULAR.queryString); | |||||
| } | |||||
| if (queryObject.has("oname")) { | |||||
| queryObjectToReturn.delete("oname"); | |||||
| queryObjectToReturn.append("search", queryObject.get("oname")); | |||||
| } | |||||
| if (queryObject.has("_des_popular")) { | |||||
| queryObjectToReturn.delete("_des_popular"); | |||||
| queryObjectToReturn.append("sortBy", sortEnum.POPULAR.queryString); | |||||
| } | |||||
| if (queryObject.has("size")) { | |||||
| queryObjectToReturn.delete("size"); | |||||
| } | |||||
| if (queryObject.has("page")) { | |||||
| if (queryObject.get("page") === "1") { | |||||
| queryObjectToReturn.delete("page"); | |||||
| return queryObjectToReturn.toString(); | |||||
| } else { | |||||
| queryObjectToReturn.delete("page"); | |||||
| return ( | |||||
| queryObjectToReturn.toString() + "&page=" + queryObject.get("page") | |||||
| ); | |||||
| } | } | ||||
| return queryObjectToReturn.toString(); | |||||
| } | |||||
| } | |||||
| return queryObjectToReturn.toString(); | |||||
| }; | |||||
| export const combineQueryStrings = (firstQuery, secondQuery) => { | |||||
| const firstQueryObject = new URLSearchParams(firstQuery); | |||||
| const secondQueryObject = new URLSearchParams(secondQuery); | |||||
| const thirdQueryObject = new URLSearchParams(secondQueryObject.toString()); | |||||
| let arrayOfProperties = Object.getOwnPropertyNames( | |||||
| Object.fromEntries(firstQueryObject) | |||||
| ); | |||||
| arrayOfProperties.forEach((property) => { | |||||
| console.log("firstQueryObject[property]: ", firstQueryObject.toString()); | |||||
| console.log("secondQueryObject[property]", secondQueryObject.toString()); | |||||
| if (!secondQueryObject.has(property)) { | |||||
| // console.log("ovde je doslo query: ", property); | |||||
| // thirdQueryObject.append(property, secondQueryObject.get(property)); | |||||
| thirdQueryObject.append(property, firstQueryObject.get(property)); | |||||
| } else { | |||||
| // console.log("ovde ispod: ", property); | |||||
| } | |||||
| }); | |||||
| console.log("thirdQueryObject[property]: ", thirdQueryObject.toString()); | |||||
| return thirdQueryObject.toString(); | |||||
| }; | |||||
| export const convertQueryStringBackend = (queryURL) => { | |||||
| const queryObject = new URLSearchParams(queryURL); | |||||
| const newQueryObject = new URLSearchParams(); | |||||
| if (queryObject.has("category")) { | |||||
| newQueryObject.append( | |||||
| "category", | |||||
| queryObject.getAll("category")[queryObject.getAll("category").length - 1] | |||||
| ); | |||||
| } | |||||
| if (queryObject.has("subcategory")) { | |||||
| newQueryObject.append( | |||||
| "subcategory", | |||||
| queryObject.getAll("subcategory")[ | |||||
| queryObject.getAll("subcategory").length - 1 | |||||
| ] | |||||
| ); | |||||
| } | |||||
| if (queryObject.has("search")) { | |||||
| newQueryObject.append( | |||||
| "oname", | |||||
| queryObject.getAll("search")[queryObject.getAll("search").length - 1] | |||||
| ); | |||||
| } | |||||
| if (queryObject.has("oname")) { | |||||
| newQueryObject.append( | |||||
| "oname", | |||||
| queryObject.getAll("oname")[queryObject.getAll("oname").length - 1] | |||||
| ); | |||||
| } | |||||
| if (queryObject.has("location")) { | |||||
| const arrayOfLocations = queryObject.getAll("location"); | |||||
| arrayOfLocations.forEach((item) => { | |||||
| newQueryObject.append("location", item); | |||||
| }); | |||||
| } | |||||
| if (queryObject.has("sortBy")) { | |||||
| newQueryObject.delete("sortBy"); | |||||
| if (queryObject.get("sortBy") === "newest") { | |||||
| newQueryObject.append("_des_date", "true"); | |||||
| } | |||||
| if (queryObject.get("sortBy") === "oldest") { | |||||
| newQueryObject.append("_des_date", "false"); | |||||
| } | |||||
| if (queryObject.get("sortBy") === "popular") { | |||||
| newQueryObject.append("_des_popular", "true"); | |||||
| } | |||||
| } | |||||
| if (queryObject.has("_des_date")) { | |||||
| newQueryObject.append("_des_date", queryObject.get("_des_date")); | |||||
| } | |||||
| if (queryObject.has("_des_popular")) { | |||||
| newQueryObject.append("_des_popular", queryObject.get("_des_popular")); | |||||
| } | |||||
| newQueryObject.append("size", "10"); | |||||
| if (!queryObject.has("page")) { | |||||
| newQueryObject.append("page", "1"); | |||||
| } else { | |||||
| newQueryObject.append("page", queryObject.get("page")); | |||||
| } | |||||
| return newQueryObject.toString(); | |||||
| }; |