Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. import React, { useState, useMemo, useEffect, useRef } from "react";
  2. import {
  3. AddOfferButton,
  4. AuthButtonsContainer,
  5. AuthButtonsDrawerContainer,
  6. DrawerContainer,
  7. EndIcon,
  8. FilterContainer,
  9. FilterIcon,
  10. HeaderContainer,
  11. LoginButton,
  12. LogoContainer,
  13. RegisterButton,
  14. SearchIcon,
  15. SearchInput,
  16. SearchInputMobile,
  17. ToggleDrawerButton,
  18. ToolsButtonsContainer,
  19. ToolsContainer,
  20. UserButton,
  21. UserName,
  22. } from "./Header.styled";
  23. import PropTypes from "prop-types";
  24. import {
  25. AppBar,
  26. Badge,
  27. Toolbar,
  28. useMediaQuery,
  29. Typography,
  30. } from "@mui/material";
  31. import { useTheme } from "@mui/system";
  32. import MenuOutlinedIcon from "@mui/icons-material/MenuOutlined";
  33. import MailIcon from "@mui/icons-material/EmailOutlined";
  34. import Autorenew from "@mui/icons-material/Autorenew";
  35. import AccountCircle from "@mui/icons-material/PersonOutlineOutlined";
  36. import Drawer from "../MUI/DrawerComponent";
  37. import { PrimaryButton } from "../Buttons/PrimaryButton/PrimaryButton";
  38. import PopoverComponent from "../Popovers/PopoverComponent";
  39. import { MyPosts } from "../Popovers/MyPosts/MyPosts";
  40. import { MyMessages } from "../Popovers/MyMessages/MyMessages";
  41. import { MyProfile } from "../Popovers/MyProfile/MyProfile";
  42. import { ReactComponent as LogoHorizontal } from "../../assets/images/svg/logo-horizontal.svg";
  43. import selectedTheme from "../../themes";
  44. import { useTranslation } from "react-i18next";
  45. import { IconButton } from "../Buttons/IconButton/IconButton";
  46. import { useDispatch, useSelector } from "react-redux";
  47. import { selectUserId } from "../../store/selectors/loginSelectors";
  48. import { useSearch } from "../../hooks/useSearch";
  49. import { selectProfileName } from "../../store/selectors/profileSelectors";
  50. import { useHistory, useRouteMatch } from "react-router-dom";
  51. import { HOME_PAGE, LOGIN_PAGE, REGISTER_PAGE } from "../../constants/pages";
  52. import useFilters from "../../hooks/useFilters";
  53. import FilterCard from "../Cards/FilterCard/FilterCard";
  54. import { useQueryString } from "../../hooks/useQueryString";
  55. import { convertQueryStringFrontend } from "../../util/helpers/queryHelpers";
  56. import { fetchMineProfile } from "../../store/actions/profile/profileActions";
  57. const Header = () => {
  58. const [openDrawer, setOpenDrawer] = useState(false);
  59. const [openFilters, setOpenFilters] = useState(false);
  60. const [showSearchBar, setShowSearchBar] = useState(true);
  61. const [numberOfFilters, setNumberOfFilters] = useState(0);
  62. const { t } = useTranslation();
  63. const theme = useTheme();
  64. const searchRef = useRef(null);
  65. const matches = useMediaQuery(theme.breakpoints.down("md"));
  66. const user = useSelector(selectUserId);
  67. const search = useSearch();
  68. const dispatch = useDispatch();
  69. const name = useSelector(selectProfileName);
  70. const history = useHistory();
  71. const routeMatch = useRouteMatch();
  72. const filters = useFilters();
  73. const searchMobileRef = useRef(null);
  74. const queryStringHook = useQueryString();
  75. useEffect(() => {
  76. dispatch(fetchMineProfile());
  77. }, []);
  78. useEffect(() => {
  79. setUserPopoverOpen(false);
  80. setUserAnchorEl(null);
  81. return () => {
  82. setUserPopoverOpen(false);
  83. setUserAnchorEl(null);
  84. };
  85. }, []);
  86. useEffect(() => {
  87. if (history.location.pathname !== "/home") {
  88. setShowSearchBar(false);
  89. } else {
  90. setShowSearchBar(true);
  91. }
  92. }, [history.location.pathname])
  93. useEffect(() => {
  94. setNumberOfFilters(filters.calculateFiltersChosen());
  95. }, [
  96. filters.selectedCategory,
  97. filters.selectedLocations,
  98. filters.selectedSubcategory,
  99. ]);
  100. useEffect(() => {
  101. if (queryStringHook.loadedFromURL) {
  102. const queryObject = new URLSearchParams(
  103. convertQueryStringFrontend(queryStringHook.queryString)
  104. );
  105. if (queryObject.has("search")) {
  106. searchRef.current.value = queryObject.get("search");
  107. searchMobileRef.current.value = queryObject.get("search");
  108. }
  109. }
  110. });
  111. const [postsPopoverOpen, setPostsPopoverOpen] = useState(false);
  112. const [postsAnchorEl, setPostsAnchorEl] = useState(null);
  113. const [msgPopoverOpen, setMsgPopoverOpen] = useState(false);
  114. const [msgAnchorEl, setMsgAnchorEl] = useState(null);
  115. const [userPopoverOpen, setUserPopoverOpen] = useState(false);
  116. const [userAnchorEl, setUserAnchorEl] = useState(null);
  117. const [shouldShow, setShouldShow] = useState(true);
  118. useEffect(() => {
  119. let shouldShowHeader = true;
  120. if (
  121. location.pathname === "/login" ||
  122. location.pathname === "/register" ||
  123. location.pathname === "/register/success" ||
  124. location.pathname === "/forgot-password" ||
  125. location.pathname === "/reset-password" ||
  126. location.pathname === "/"
  127. ) {
  128. shouldShowHeader = false;
  129. }
  130. if (location.pathname === "/" && user?.length === 0) {
  131. shouldShowHeader = false;
  132. }
  133. setShouldShow(shouldShowHeader);
  134. }, [location.pathname, user, routeMatch]);
  135. const handleToggleDrawer = () => {
  136. setOpenDrawer(!openDrawer);
  137. };
  138. const handleNavigateLogin = () => {
  139. setShouldShow(false);
  140. history.push(LOGIN_PAGE);
  141. };
  142. const handleNavigateRegister = () => {
  143. setShouldShow(false);
  144. history.push(REGISTER_PAGE);
  145. };
  146. const drawerContent = useMemo(
  147. () => (
  148. <DrawerContainer>
  149. {user ? (
  150. <React.Fragment>
  151. <PrimaryButton
  152. type="submit"
  153. variant="contained"
  154. height="36px"
  155. fullWidth
  156. buttoncolor={selectedTheme.primaryYellow}
  157. textcolor="black"
  158. onClick={() => handleToggleDrawer()}
  159. >
  160. {t("header.addOffer")}
  161. </PrimaryButton>
  162. <ToolsContainer mobile>
  163. <IconButton
  164. onClick={(e) => {
  165. setPostsPopoverOpen(true);
  166. setPostsAnchorEl(e.currentTarget);
  167. }}
  168. sx={{ borderRadius: "4px" }}
  169. >
  170. <Autorenew />
  171. <Typography sx={{ ml: 2 }}>Moje objave</Typography>
  172. </IconButton>
  173. <IconButton
  174. onClick={(e) => {
  175. setMsgPopoverOpen(true);
  176. setMsgAnchorEl(e.currentTarget);
  177. }}
  178. sx={{ borderRadius: "4px" }}
  179. >
  180. <Badge badgeContent={3} color="primary">
  181. <MailIcon color="action" />
  182. </Badge>
  183. <Typography sx={{ ml: 2 }}>Moje poruke</Typography>
  184. </IconButton>
  185. <IconButton
  186. onClick={(e) => {
  187. setUserPopoverOpen(true);
  188. setUserAnchorEl(e.currentTarget);
  189. }}
  190. sx={{ borderRadius: "4px" }}
  191. >
  192. <AccountCircle />
  193. <Typography sx={{ ml: 2 }}>Moj profil</Typography>
  194. </IconButton>
  195. </ToolsContainer>
  196. </React.Fragment>
  197. ) : (
  198. <AuthButtonsDrawerContainer>
  199. <RegisterButton
  200. type="submit"
  201. variant="contained"
  202. height="36px"
  203. fullWidth
  204. buttoncolor={selectedTheme.primaryYellow}
  205. textcolor={selectedTheme.primaryDarkText}
  206. onClick={handleNavigateRegister}
  207. >
  208. {t("register.headerTitle")}
  209. </RegisterButton>
  210. <LoginButton
  211. type="submit"
  212. variant="contained"
  213. height="36px"
  214. fullWidth
  215. buttoncolor={selectedTheme.primaryPurple}
  216. textcolor={selectedTheme.primaryIconBackgroundColor}
  217. onClick={handleNavigateLogin}
  218. >
  219. {t("login.headerTitle")}
  220. </LoginButton>
  221. </AuthButtonsDrawerContainer>
  222. )}
  223. </DrawerContainer>
  224. ),
  225. [handleToggleDrawer]
  226. );
  227. let listener;
  228. const handleFocusSearch = () => {
  229. listener = (event) => {
  230. if (event.keyCode === 13) {
  231. event.preventDefault();
  232. handleSearch(searchRef.current.value);
  233. }
  234. };
  235. searchRef.current.addEventListener("keyup", listener);
  236. };
  237. const handleBlurSearch = () => {
  238. searchRef.current.removeEventListener("keyup", listener);
  239. };
  240. const handleSearch = (value) => {
  241. console.log(value);
  242. if (value.length === 0) return;
  243. search.searchOffers(value);
  244. };
  245. const toggleFilters = () => {
  246. setOpenFilters((prevState) => !prevState);
  247. };
  248. const handleLogoClick = () => {
  249. history.push(HOME_PAGE);
  250. }
  251. return (
  252. <HeaderContainer style={{ display: shouldShow ? "block" : "none" }}>
  253. <AppBar
  254. elevation={0}
  255. position="fixed"
  256. // positionFixed
  257. sx={{ backgroundColor: "white" }}
  258. >
  259. <Toolbar>
  260. <ToolsContainer>
  261. <LogoContainer onClick={() => handleLogoClick()}>
  262. <LogoHorizontal />
  263. </LogoContainer>
  264. {matches && (
  265. <Drawer
  266. open={openDrawer}
  267. toggleOpen={handleToggleDrawer}
  268. content={drawerContent}
  269. />
  270. )}
  271. <SearchInput
  272. fullWidth
  273. InputProps={{
  274. endAdornment: (
  275. <EndIcon size="36px">
  276. <SearchIcon
  277. onClick={() => handleSearch(searchRef.current.value)}
  278. />
  279. </EndIcon>
  280. ),
  281. }}
  282. placeholder={t("header.searchOffers")}
  283. onFocus={handleFocusSearch}
  284. onBlur={handleBlurSearch}
  285. ref={searchRef}
  286. />
  287. {user ? (
  288. <ToolsButtonsContainer mobile={matches}>
  289. {matches ? (
  290. <ToggleDrawerButton>
  291. <IconButton onClick={handleToggleDrawer}>
  292. <MenuOutlinedIcon />
  293. </IconButton>
  294. </ToggleDrawerButton>
  295. ) : (
  296. <React.Fragment>
  297. <AddOfferButton
  298. type="submit"
  299. variant="contained"
  300. fullWidth
  301. buttoncolor={selectedTheme.primaryYellow}
  302. textcolor={selectedTheme.primaryDarkText}
  303. onClick={() => {
  304. setUserPopoverOpen(false);
  305. setUserAnchorEl(null);
  306. }}
  307. >
  308. {t("header.addOffer")}
  309. </AddOfferButton>
  310. <IconButton
  311. onClick={(e) => {
  312. setPostsPopoverOpen(true);
  313. setPostsAnchorEl(e.currentTarget);
  314. }}
  315. style={{
  316. background: selectedTheme.primaryIconBackgroundColor,
  317. color: selectedTheme.primaryPurple,
  318. }}
  319. >
  320. <Autorenew />
  321. </IconButton>
  322. <IconButton
  323. onClick={(e) => {
  324. setMsgPopoverOpen(true);
  325. setMsgAnchorEl(e.currentTarget);
  326. }}
  327. style={{
  328. background: selectedTheme.primaryIconBackgroundColor,
  329. color: selectedTheme.primaryPurple,
  330. }}
  331. >
  332. <Badge badgeContent={3} color="primary">
  333. <MailIcon />
  334. </Badge>
  335. </IconButton>
  336. <UserButton
  337. onClick={(e) => {
  338. setUserPopoverOpen(true);
  339. setUserAnchorEl(e.currentTarget);
  340. }}
  341. >
  342. <UserName>{name}</UserName>
  343. <IconButton
  344. style={{
  345. background: selectedTheme.primaryIconBackgroundColor,
  346. color: selectedTheme.primaryPurple,
  347. }}
  348. >
  349. <AccountCircle />
  350. </IconButton>
  351. </UserButton>
  352. </React.Fragment>
  353. )}
  354. </ToolsButtonsContainer>
  355. ) : (
  356. <AuthButtonsContainer mobile={matches}>
  357. {matches ? (
  358. <ToggleDrawerButton>
  359. <IconButton onClick={handleToggleDrawer}>
  360. <MenuOutlinedIcon />
  361. </IconButton>
  362. </ToggleDrawerButton>
  363. ) : (
  364. <React.Fragment>
  365. <LoginButton
  366. type="submit"
  367. variant="contained"
  368. fullWidth
  369. buttoncolor={selectedTheme.primaryPurple}
  370. textcolor={selectedTheme.offerBackgroundColor}
  371. onClick={handleNavigateLogin}
  372. >
  373. {t("login.headerTitle")}
  374. </LoginButton>
  375. <RegisterButton
  376. type="submit"
  377. variant="contained"
  378. fullWidth
  379. buttoncolor={selectedTheme.primaryYellow}
  380. textcolor={selectedTheme.primaryDarkText}
  381. onClick={handleNavigateRegister}
  382. >
  383. {t("register.headerTitle")}
  384. </RegisterButton>
  385. </React.Fragment>
  386. )}
  387. </AuthButtonsContainer>
  388. )}
  389. </ToolsContainer>
  390. </Toolbar>
  391. {user && (
  392. <React.Fragment>
  393. <PopoverComponent
  394. anchorEl={postsAnchorEl}
  395. open={postsPopoverOpen}
  396. onClose={() => {
  397. setPostsPopoverOpen(false);
  398. setPostsAnchorEl(null);
  399. }}
  400. content={<MyPosts />}
  401. />
  402. <PopoverComponent
  403. anchorEl={msgAnchorEl}
  404. open={msgPopoverOpen}
  405. onClose={() => {
  406. setMsgPopoverOpen(false);
  407. setMsgAnchorEl(null);
  408. }}
  409. content={<MyMessages />}
  410. />
  411. <PopoverComponent
  412. anchorEl={userAnchorEl}
  413. open={userPopoverOpen}
  414. onClose={() => {
  415. setUserPopoverOpen(false);
  416. setUserAnchorEl(null);
  417. }}
  418. content={<MyProfile />}
  419. />
  420. </React.Fragment>
  421. )}
  422. </AppBar>
  423. <SearchInputMobile
  424. fullWidth
  425. shouldShow={showSearchBar}
  426. ref={searchMobileRef}
  427. InputProps={{
  428. endAdornment: (
  429. <React.Fragment>
  430. <FilterContainer number={numberOfFilters}>
  431. <FilterIcon onClick={toggleFilters} />
  432. </FilterContainer>
  433. <EndIcon size="36px">
  434. <SearchIcon
  435. onClick={() => handleSearch(searchMobileRef.current.value)}
  436. />
  437. </EndIcon>
  438. </React.Fragment>
  439. ),
  440. }}
  441. placeholder={t("header.searchOffers")}
  442. italicPlaceholder
  443. onFocus={handleFocusSearch}
  444. onBlur={handleBlurSearch}
  445. />
  446. <FilterCard
  447. responsive={true}
  448. responsiveOpen={openFilters}
  449. closeResponsive={toggleFilters}
  450. />
  451. </HeaderContainer>
  452. );
  453. };
  454. Header.propTypes = {
  455. isGrid: PropTypes.bool,
  456. };
  457. export default Header;