Sfoglia il codice sorgente

Ready for create-offer

feature/code-cleanup-joca
Djordje Mitrovic 3 anni fa
parent
commit
c105a3cf39
53 ha cambiato i file con 588 aggiunte e 180 eliminazioni
  1. 0
    4
      src/assets/styles/_base.scss
  2. 2
    2
      src/components/Cards/CreateOfferCard/CreateOffer.js
  3. 0
    1
      src/components/Cards/CreateOfferCard/FirstPart/FirstPartCreateOffer.js
  4. 0
    1
      src/components/Cards/CreateOfferCard/SecondPart/SecondPartCreateOffer.js
  5. 0
    1
      src/components/Cards/FilterCard/FilterDropdown/Radio/FilterRadioDropdown.js
  6. 27
    25
      src/components/Header/Header.js
  7. 13
    0
      src/components/Header/Header.styled.js
  8. 9
    10
      src/components/MarketPlace/Header/Header.js
  9. 1
    3
      src/components/MarketPlace/MarketPlace.js
  10. 13
    7
      src/components/MarketPlace/Offers/Offers.js
  11. 38
    7
      src/components/Popovers/MyMessages/MyMessages.js
  12. 6
    1
      src/components/Router/PrivateRoute.js
  13. 0
    1
      src/components/Select/Option/Option.js
  14. 2
    0
      src/components/Select/Select.js
  15. 8
    7
      src/components/TextFields/TextField/TextField.js
  16. 48
    66
      src/hooks/useFilters.js
  17. 17
    0
      src/hooks/useSearch.js
  18. 106
    0
      src/hooks/useSorting.js
  19. 1
    0
      src/layouts/MainLayout/MainLayout.styled.js
  20. 1
    0
      src/layouts/ProfileLayout/ProfileLayout.styled.js
  21. 7
    4
      src/pages/LoginPage/LoginPage.js
  22. 1
    0
      src/request/apiEndpoints.js
  23. 5
    0
      src/request/chatRequest.js
  24. 0
    1
      src/request/index.js
  25. 5
    0
      src/request/profileRequest.js
  26. 7
    0
      src/store/actions/chat/chatActionConstants.js
  27. 10
    0
      src/store/actions/chat/chatActions.js
  28. 1
    0
      src/store/actions/filters/filtersActionConstants.js
  29. 5
    1
      src/store/actions/filters/filtersActions.js
  30. 1
    1
      src/store/actions/login/loginActions.js
  31. 5
    1
      src/store/actions/offers/offersActionConstants.js
  32. 9
    5
      src/store/actions/offers/offersActions.js
  33. 8
    0
      src/store/actions/profile/profileActionConstants.js
  34. 19
    0
      src/store/actions/profile/profileActions.js
  35. 20
    0
      src/store/reducers/chat/chatReducer.js
  36. 12
    0
      src/store/reducers/filters/filtersReducer.js
  37. 14
    0
      src/store/reducers/index.js
  38. 14
    5
      src/store/reducers/offers/offersReducer.js
  39. 20
    0
      src/store/reducers/profile/profileReducer.js
  40. 20
    0
      src/store/saga/chatSaga.js
  41. 0
    1
      src/store/saga/forgotPasswordSaga.js
  42. 5
    1
      src/store/saga/index.js
  43. 0
    1
      src/store/saga/locationsSaga.js
  44. 4
    4
      src/store/saga/loginSaga.js
  45. 29
    17
      src/store/saga/offersSaga.js
  46. 22
    0
      src/store/saga/profileSaga.js
  47. 8
    0
      src/store/selectors/chatSelectors.js
  48. 4
    0
      src/store/selectors/filtersSelectors.js
  49. 4
    2
      src/store/selectors/loginSelectors.js
  50. 4
    0
      src/store/selectors/offersSelectors.js
  51. 12
    0
      src/store/selectors/profileSelectors.js
  52. 2
    0
      src/util/helpers/authScopeHelpers.js
  53. 19
    0
      src/util/helpers/queryHelpers.js

+ 0
- 4
src/assets/styles/_base.scss Vedi File

@@ -34,10 +34,6 @@ body,
flex: 1 0 auto;
}

#root {
margin-top: 80px;
}

input[type='search']::-webkit-search-decoration,
input[type='search']::-webkit-search-cancel-button,
input[type='search']::-webkit-search-results-button,

+ 2
- 2
src/components/Cards/CreateOfferCard/CreateOffer.js Vedi File

@@ -6,7 +6,7 @@ import { useDispatch, useSelector } from "react-redux";
import { NavLink } from "react-router-dom";
import * as Yup from "yup";
import { useTranslation } from "react-i18next";
import { fetchUser } from "../../../store/actions/login/loginActions";
import { fetchLogin } from "../../../store/actions/login/loginActions";
import { FORGOT_PASSWORD_PAGE, HOME_PAGE } from "../../../constants/pages";
import { ReactComponent as VisibilityOn } from "../../../assets/images/svg/eye-striked.svg";
import { ReactComponent as VisibilityOff } from "../../../assets/images/svg/eye.svg";
@@ -67,7 +67,7 @@ const CreateOffer = ({ history }) => {
const handleSubmit = (values) => {
const { username: email, password: password } = values;
dispatch(
fetchUser({
fetchLogin({
email,
password,
handleApiResponseSuccess,

+ 0
- 1
src/components/Cards/CreateOfferCard/FirstPart/FirstPartCreateOffer.js Vedi File

@@ -17,7 +17,6 @@ import { SelectField } from "../CreateOffer.styled";
const FirstPartCreateOffer = (props) => {
const { t } = useTranslation();
const handleSubmit = (values) => {
console.log(values);
props.handleNext(values);
};
const formik = useFormik({

+ 0
- 1
src/components/Cards/CreateOfferCard/SecondPart/SecondPartCreateOffer.js Vedi File

@@ -17,7 +17,6 @@ import { conditionSelectEnum } from "../../../../enums/conditionEnum";
const SecondPartCreateOffer = () => {
const [images, setImages] = useState([null, null, null]); // 3 images
const setImage = (index, image) => {
console.log(images);
setImages((prevState) => {
let newState = [...prevState];
newState[index] = image;

+ 0
- 1
src/components/Cards/FilterCard/FilterDropdown/Radio/FilterRadioDropdown.js Vedi File

@@ -107,7 +107,6 @@ const FilterRadioDropdown = (props) => {
</DropdownItem>
)}
{dataToShow.map((item) => {
{/* console.log("item: ", item); */}
return (
<DropdownItem
key={item.name}

+ 27
- 25
src/components/Header/Header.js Vedi File

@@ -36,6 +36,8 @@ import { IconButton } from "../Buttons/IconButton/IconButton";
import { Icon } from "../Icon/Icon";
import { useSelector } from "react-redux";
import { selectJWTToken } from "../../store/selectors/loginSelectors";
import { useSearch } from "../../hooks/useSearch";
import { selectProfileName } from "../../store/selectors/profileSelectors";

const Header = () => {
const [openDrawer, setOpenDrawer] = useState(false);
@@ -44,12 +46,14 @@ const Header = () => {
const searchRef = useRef(null);
const matches = useMediaQuery(theme.breakpoints.down("md"));
const user = useSelector(selectJWTToken);

const search = useSearch();
const name = useSelector(selectProfileName);

const handleToggleDrawer = () => {
setOpenDrawer(!openDrawer);
};


const [postsPopoverOpen, setPostsPopoverOpen] = useState(false);
const [postsAnchorEl, setPostsAnchorEl] = useState(null);

@@ -71,28 +75,25 @@ const Header = () => {
) {
shouldShowHeader = false;
}
console.log(user);
if (location.pathname === "/" && user.JwtToken?.length === 0) {
shouldShowHeader = false;
}
setShouldShow(shouldShowHeader);
}, [location, user]);

// let listener;
let listener;
const handleFocusSearch = () => {
// console.log('focus');
// listener = (event) => {
// if (event.keyCode === 13) {
// event.preventDefault();
// console.log('detektovano');
// }
// }
// searchRef.current.addEventListener('keyup', listener);
}
listener = (event) => {
if (event.keyCode === 13) {
event.preventDefault();
search.searchOffers(searchRef.current.value)
}
}
searchRef.current.addEventListener('keyup', listener);
};
const handleBlurSearch = () => {
// console.log('blur');
// searchRef.current.removeEventListener('keyup', listener);
}
searchRef.current.removeEventListener('keyup', listener);
};

const drawerContent = useMemo(
() => (
@@ -150,9 +151,10 @@ const Header = () => {
return (
<AppBar
elevation={0}
positionFixed
sx={{ backgroundColor: "white"}}
style={{display: shouldShow ? "flex" : "none"}}
position="fixed"
// positionFixed
sx={{ backgroundColor: "white" }}
style={{ display: shouldShow ? "flex" : "none" }}
>
<Toolbar>
<ToolsContainer>
@@ -224,13 +226,13 @@ const Header = () => {
<MailIcon />
</Badge>
</IconButton>
<UserButton onClick={(e) => {
setUserPopoverOpen(true);
setUserAnchorEl(e.currentTarget);
}}>
<UserName>
Username korisnika
</UserName>
<UserButton
onClick={(e) => {
setUserPopoverOpen(true);
setUserAnchorEl(e.currentTarget);
}}
>
<UserName>{name}</UserName>
<IconButton
style={{
background: selectedTheme.primaryIconBackgroundColor,

+ 13
- 0
src/components/Header/Header.styled.js Vedi File

@@ -38,6 +38,7 @@ export const DrawerContainer = styled(Box)`
export const ToolsContainer = styled(Box)`
display: flex;
flex-direction: row;

justify-content: ${(props) => (props.mobile ? "center" : "space-between")};
align-items: ${(props) => (props.mobile ? "start" : "center")};
${(props) => !props.mobile && `width: 100%;`}
@@ -52,10 +53,22 @@ export const LogoContainer = styled(Box)`
`;
export const ToolsButtonsContainer = styled(Box)`
display: flex;
flex: 4;
justify-content: space-between;
min-width: ${props => props.mobile ? "40px" : "600px"};
max-width: 600px;
align-items: center;
flex-wrap: nowrap;
@media (max-width: 1200px) {
min-width: 400px;
}
@media (max-width: 950px) {
min-width: 250px;
}
@media (max-width: 800px) {
min-width: 0px;
width: 0px;
}
`;
export const ToggleDrawerButton = styled(Box)`
max-width: 40px;

+ 9
- 10
src/components/MarketPlace/Header/Header.js Vedi File

@@ -5,7 +5,6 @@ import {
HeaderButtons,
HeaderContainer,
HeaderLocation,
// HeaderLocation,
HeaderOptions,
HeaderSelect,
IconStyled,
@@ -14,13 +13,10 @@ import { ReactComponent as GridSquare } from "../../../assets/images/svg/offer-g
import { ReactComponent as GridLine } from "../../../assets/images/svg/offer-grid-line.svg";
import { ReactComponent as Down } from "../../../assets/images/svg/down-arrow.svg";
import selectedTheme from "../../../themes";
// import { useSelector } from "react-redux";
// import { selectFilters } from "../../../store/selectors/filtersSelectors";
// import Mockupdata from "../../Cards/FilterCard/Mockupdata";
import Option from "../../Select/Option/Option";
// import { useHistory, useLocation, useRouteMatch } from "react-router-dom";
import { sortEnum } from "../../../enums/sortEnum";
import useFilters from "../../../hooks/useFilters";
import useSorting from "../../../hooks/useSorting";

const DownArrow = (props) => (
<IconStyled {...props}>
@@ -30,13 +26,18 @@ const DownArrow = (props) => (

const Header = (props) => {
const filters = useFilters();
const sorting = useSorting();
const [sortOption, setSortOption] = useState(sortEnum.INITIAL);
const [headerString, setHeaderString] = useState("SVE KATEGORIJE");

useEffect(() => {
setSortOption(sorting.selectedSortOption);
}, [sorting.selectedSortOption]);

useEffect(() => {
if (filters.isApplied) {
let headerStringLocal = "SVE KATEGORIJE";
if (filters.selectedCategory?.name) {
console.log(filters.selectedCategory);
headerStringLocal = filters.selectedCategory.name;
if (filters.selectedSubcategory?.name) {
headerStringLocal += ` | ${filters.selectedSubcategory.name}`;
@@ -66,7 +67,7 @@ const Header = (props) => {
for (const sortOption in sortEnum) {
if (sortEnum[sortOption].value === event.target.value) {
chosenOption = sortEnum[sortOption];
filters.setSelectedSortOption(chosenOption);
sorting.setSelectedSortOption(chosenOption);
}
}
};
@@ -98,9 +99,7 @@ const Header = (props) => {
</HeaderButton>
</HeaderButtons>
<HeaderSelect
defaultValue={
filters.selectedSortOption ? filters.selectedSortOption.value : 0
}
value={sortOption?.value ? sortOption.value : sortEnum.INITIAL.value}
IconComponent={DownArrow}
width="209px"
height="34px"

+ 1
- 3
src/components/MarketPlace/MarketPlace.js Vedi File

@@ -3,15 +3,13 @@ import PropTypes from "prop-types";
import { MarketPlaceContainer } from "./MarketPlace.styled";
import Header from "./Header/Header";
import Offers from "./Offers/Offers";
import useFilters from "../../hooks/useFilters";

const MarketPlace = () => {
const [isGrid, setIsGrid] = useState(false);
const filters = useFilters();

return (
<MarketPlaceContainer>
<Header isGrid={isGrid} setIsGrid={setIsGrid} filters={filters} />
<Header isGrid={isGrid} setIsGrid={setIsGrid} />
<Offers isGrid={isGrid} />
</MarketPlaceContainer>
);

+ 13
- 7
src/components/MarketPlace/Offers/Offers.js Vedi File

@@ -10,6 +10,7 @@ import { useDispatch, useSelector } from "react-redux";
import {
selectNoMoreOffers,
selectOffers,
selectPinnedOffers,
} from "../../../store/selectors/offersSelectors";
import useFilters from "../../../hooks/useFilters";

@@ -17,6 +18,7 @@ const Offers = (props) => {
const filters = useFilters();
const [page, setPage] = useState(2);
const [initialLoad, setInitialLoad] = useState(true);
const pinnedOffers = useSelector(selectPinnedOffers);
const offers = useSelector(selectOffers);
const dispatch = useDispatch();
const offersRef = useRef(null);
@@ -47,8 +49,8 @@ const Offers = (props) => {
}, [page, noMoreOffersStatus, filters.queryString, timeout, offers]);

useEffect(() => {
setPage(Math.floor(offers.length / 10) + 1);
}, [offers]);
setPage(Math.floor((offers.length + pinnedOffers.length ) / 10) + 1);
}, [offers, pinnedOffers]);

useEffect(() => {
dispatch(fetchOffers({ page: 1 }));
@@ -68,11 +70,15 @@ const Offers = (props) => {
}, [filters.queryString, initialLoad]);

return (
<OffersContainer
ref={offersRef}
onScroll={() => console.log("proba")}
onClick={() => console.log(offersRef)}
>
<OffersContainer ref={offersRef}>
<>
{pinnedOffers != undefined &&
pinnedOffers.map((item) => {
return (
<OfferCard key={item._id} offer={item} halfwidth={props.isGrid} />
);
})}
</>
{offers != undefined &&
offers.map((item) => {
return (

+ 38
- 7
src/components/Popovers/MyMessages/MyMessages.js Vedi File

@@ -1,5 +1,9 @@
import React from "react";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
import { fetchChats } from "../../../store/actions/chat/chatActions";
import { selectLatestChats } from "../../../store/selectors/chatSelectors";
import { selectUserId } from "../../../store/selectors/loginSelectors";
import HeaderPopover from "../HeaderPopover/HeaderPopover";

const dummyData1 = [
@@ -7,22 +11,49 @@ const dummyData1 = [
alt: "Remy Sharp",
src: "/static/images/avatar/1.jpg",
title: "Coca-Cola",
text: "Kompresor je stigao. Samo..."
text: "Kompresor je stigao. Samo...",
},
{
alt: "Travis Howard",
src: "/static/images/avatar/2.jpg",
title: "Voda Vrnjci",
text: "Poslao sam vodu. Ukupno i..."
}
]
text: "Poslao sam vodu. Ukupno i...",
},
];

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

export const MyMessages = () => {
const {t} = useTranslation();
const { t } = useTranslation();
const dispatch = useDispatch();
const userId = useSelector(selectUserId);
const chats = useSelector(selectLatestChats);
convertMessages(chats);
useEffect(() => {
dispatch(fetchChats(userId));
}, []);
return (
<HeaderPopover
title={t("header.myMessages")}
items={dummyData1}
buttonText={t("header.checkEverything")} />
buttonText={t("header.checkEverything")}
/>
);
};

+ 6
- 1
src/components/Router/PrivateRoute.js Vedi File

@@ -1,18 +1,23 @@
import React, { useEffect } from 'react';
import { Redirect, Route } from 'react-router';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { authenticateUser } from '../../store/actions/login/loginActions';
// import { selectIsUserAuthenticated } from '../../store/selectors/userSelectors';
import { LOGIN_PAGE } from '../../constants/pages';
import { fetchProfile } from '../../store/actions/profile/profileActions';
import { selectUserId } from '../../store/selectors/loginSelectors';

const PrivateRoute = ({ ...props }) => {
const dispatch = useDispatch();
const userId = useSelector(selectUserId);
// const isUserAuthenticated = useSelector(selectIsUserAuthenticated);
const isUserAuthenticated = true;

useEffect(() => {
if (!isUserAuthenticated) {
dispatch(authenticateUser());
} else {
dispatch(fetchProfile(userId))
}
}, [isUserAuthenticated]); // eslint-disable-line


+ 0
- 1
src/components/Select/Option/Option.js Vedi File

@@ -3,7 +3,6 @@ import PropTypes from 'prop-types'
import { OptionIcon, OptionStyled } from './Option.styled'

const Option = props => {
console.log(props)
return (
<OptionStyled {...props} value={props.value} >
{props.startIcon ? (

+ 2
- 0
src/components/Select/Select.js Vedi File

@@ -16,6 +16,7 @@ const Select = (props) => {
fullWidth={props.fullwidth}
open={isOpened}
onClick={() => handleOpen()}
value={props.value}
width={props.width}
height={props.height}
className={props.className}
@@ -39,6 +40,7 @@ Select.propTypes = {
defaultValue: PropTypes.number,
className: PropTypes.string,
onChange: PropTypes.func,
value: PropTypes.any,
};
Select.defaultProps = {
fullwidth: true,

+ 8
- 7
src/components/TextFields/TextField/TextField.js Vedi File

@@ -1,8 +1,8 @@
import React, { useEffect, useRef, useState } from "react";
import React, { useEffect, useState } from "react";
import { TextFieldContainer, TextFieldStyled } from "./TextField.styled";
import PropTypes from "prop-types";

export const TextField = (props) => {
export const TextField = React.forwardRef((props, ref) => {
const [isFieldEmpty, setIsFieldEmpty] = useState(true);

// for italicPlaceholder
@@ -13,7 +13,6 @@ export const TextField = (props) => {
setIsFieldEmpty(false);
}
}, [props.value]);
const textInputRef = useRef();

return (
<TextFieldContainer
@@ -22,6 +21,7 @@ export const TextField = (props) => {
height={props.height}
>
<TextFieldStyled
inputRef={ref}
placeholder={props.placeholder}
width={props.width}
height={props.height}
@@ -46,14 +46,15 @@ export const TextField = (props) => {
italicplaceholder={(props.italicPlaceholder && isFieldEmpty) ? "true" : "false"}
ref={textInputRef}
focused={props.focused}
>
{props.children}
</TextFieldStyled>
</TextFieldContainer>
);
};
});

TextField.displayName = "TextField";

TextField.propTypes = {
history: PropTypes.shape({
@@ -87,8 +88,8 @@ TextField.propTypes = {
ref: PropTypes.any,
minRows: PropTypes.number,
multiline: PropTypes.bool,
onFocus: PropTypes.onFocus,
onBlur: PropTypes.onBlur,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
focused: PropTypes.bool,
InputProps: PropTypes.shape({
startAdornment: PropTypes.node,

+ 48
- 66
src/hooks/useFilters.js Vedi File

@@ -2,14 +2,13 @@ import { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { useSelector } from "react-redux";
import { useHistory } from "react-router-dom";
import { sortEnum } from "../enums/sortEnum";
import { fetchCategories } from "../store/actions/categories/categoriesActions";
import {
setFilteredCategory,
setFilteredLocations,
setFilteredSortOption,
setFilteredSubcategory,
setIsAppliedStatus,
setQueryString,
} from "../store/actions/filters/filtersActions";
import { fetchLocations } from "../store/actions/locations/locationsActions";
import {
@@ -18,22 +17,22 @@ import {
} from "../store/selectors/categoriesSelectors";
import {
selectAppliedStatus,
selectQueryString,
selectSelectedCategory,
selectSelectedLocations,
selectSelectedSortOption,
selectSelectedSubcategory,
} from "../store/selectors/filtersSelectors";
import qs from "query-string";
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";

const useFilters = () => {
const [queryString, setQueryString] = useState();
const queryString = useSelector(selectQueryString);
const selectedCategory = useSelector(selectSelectedCategory);
const selectedSubcategory = useSelector(selectSelectedSubcategory);
const selectedLocations = useSelector(selectSelectedLocations);
const selectedSortOption = useSelector(selectSelectedSortOption);
const [loadedFromQS, setLoadedFromQS] = useState(false);
const [loaded, setLoadedStatus] = useState(false);
const isApplied = useSelector(selectAppliedStatus);
@@ -43,7 +42,6 @@ const useFilters = () => {
selectSubcategories(selectedCategory?.name)
);
const locations = useSelector(selectLocations);
const sortOptions = sortEnum;
const dispatch = useDispatch();
useEffect(() => {
if (!loaded) {
@@ -86,17 +84,17 @@ const useFilters = () => {
setSelectedLocations([...locationsToPush]);
}
// For future changes if needed
if (queryObject._des_date || queryObject._des_popular) {
if (queryObject._des_date === "false") {
setSelectedSortOption(sortOptions.OLD);
}
if (queryObject._des_date === "true") {
setSelectedSortOption(sortOptions.NEW);
}
if (queryObject._des_popular === "true") {
setSelectedSortOption(sortOptions.POPULAR);
}
}
// 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);
// }
// }
setLoadedFromQS(true);
dispatch(setIsAppliedStatus(true));
}
@@ -105,59 +103,22 @@ const useFilters = () => {

useEffect(() => {
if (loadedFromQS) {
let qsArray = [];
if (selectedCategory && selectedCategory?._id !== 0) {
qsArray.push("category=" + encodeURIComponent(selectedCategory.name));
}
if (selectedSubcategory && selectedSubcategory?._id !== 0) {
qsArray.push(
"subcategory=" + encodeURIComponent(selectedSubcategory.name)
);
}
if (selectedLocations && selectedLocations?.length > 0) {
selectedLocations.forEach((location) => {
qsArray.push("location=" + encodeURIComponent(location.city));
});
}
if (selectedSortOption) {
let _des_date = null;
let _des_popular = null;
if (selectedSortOption === sortOptions.NEW) {
_des_date = true;
}
if (selectedSortOption === sortOptions.OLD) {
_des_date = false;
}
if (selectedSortOption === sortOptions.POPULAR) {
_des_popular = true;
}
if (_des_date !== null)
qsArray.push("_des_date=" + encodeURIComponent(_des_date));
if (_des_popular !== null)
qsArray.push("_des_popular=" + encodeURIComponent(_des_popular));
}
let qsStringFromArray = "?" + qsArray.join("&");
console.log(qsStringFromArray);
setQueryString(qsStringFromArray);
makeQueryString();
}
}, [
selectedCategory,
selectedLocations,
selectedSortOption,
selectedSubcategory,
loadedFromQS,
]);

useEffect(() => {
console.log("QS: ", queryString);
}, [queryString]);

// Apply everything
const applyFilters = () => {
dispatch(setIsAppliedStatus(true));
dispatch(fetchOffers({ queryString: queryString }));
history.push({
pathname: HOME_PAGE,
search: queryString,
search: convertQueryString(queryString),
});
window.scrollTo({
top: 0,
@@ -165,12 +126,41 @@ const useFilters = () => {
});
};

// Clear function
const clearFilters = () => {
setSelectedLocations([]);
setSelectedSubcategory();
setSelectedCategory();
};

// Helper function
const makeQueryString = () => {
let qsArray = [];
if (selectedCategory && selectedCategory?._id !== 0) {
qsArray.push("category=" + encodeURIComponent(selectedCategory.name));
}
if (selectedSubcategory && selectedSubcategory?._id !== 0) {
qsArray.push(
"subcategory=" + encodeURIComponent(selectedSubcategory.name)
);
}
if (selectedLocations && selectedLocations?.length > 0) {
selectedLocations.forEach((location) => {
qsArray.push("location=" + encodeURIComponent(location.city));
});
}
let qsStringFromArray = "?" + qsArray.join("&");
// if (queryString.length > 1) {
// dispatch(setQueryString(queryString + "&" + qsStringFromArray));
// } else {
// let queryStringEdited = new URLSearchParams(qsStringFromArray);

dispatch(setQueryString(qsStringFromArray));
// }
};


// Setters
const setSelectedCategory = (payload) => {
if (isApplied !== false) {
dispatch(setIsAppliedStatus(false));
@@ -189,12 +179,6 @@ const useFilters = () => {
}
dispatch(setFilteredLocations(payload));
};
const setSelectedSortOption = (payload) => {
if (isApplied !== false) {
dispatch(setIsAppliedStatus(false));
}
dispatch(setFilteredSortOption(payload));
};
return {
selectedCategory,
setSelectedCategory,
@@ -202,16 +186,14 @@ const useFilters = () => {
setSelectedSubcategory,
selectedLocations,
setSelectedLocations,
selectedSortOption,
setSelectedSortOption,
categories,
subcategories,
locations,
sortOptions,
queryString,
applyFilters,
clearFilters,
isApplied,
makeQueryString
};
};


+ 17
- 0
src/hooks/useSearch.js Vedi File

@@ -0,0 +1,17 @@
import { useDispatch, useSelector } from "react-redux";
import { fetchOffers } from "../store/actions/offers/offersActions";
import { selectQueryString } from "../store/selectors/filtersSelectors";

export const useSearch = () => {
const dispatch = useDispatch();
const queryString = useSelector(selectQueryString);
const searchOffers = (searchString) => {
const queryObject = new URLSearchParams(queryString);
queryObject.set('oname', searchString);
dispatch(fetchOffers({queryString: "?" + queryObject.toString()}));
}

return {
searchOffers
}
}

+ 106
- 0
src/hooks/useSorting.js Vedi File

@@ -0,0 +1,106 @@
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useHistory } from "react-router-dom";
import { HOME_PAGE } from "../constants/pages";
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";

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 selectedSortOption = useSelector(selectSelectedSortOption);
const sortOptions = sortEnum;

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)
}
} else {
setSelectedSortOption(sortOptions.INITIAL)
}
setGlobalQueryString(qs.stringify(queryObjectFromUrl));
filters.makeQueryString();
}, [history.location.search]);

const setSelectedSortOption = (payload) => {
dispatch(setFilteredSortOption(payload));
let _des_date = null;
let _des_popular = null;
if (payload === sortOptions.NEW) {
setGlobalQueryString(`sortBy=${sortOptions.NEW.queryString}`);
_des_date = true;
}
if (payload === sortOptions.OLD) {
setGlobalQueryString(`sortBy=${sortOptions.OLD.queryString}`);
_des_date = false;
}
if (payload === sortOptions.POPULAR) {
setGlobalQueryString(`sortBy=${sortOptions.POPULAR.queryString}`);
_des_popular = true;
}
if (_des_date !== null) {
setLocalQueryString(`_des_date=${_des_date}`);
}
if (_des_popular !== null) {
setLocalQueryString(`_des_popular=${_des_popular}`);
}
};
return {
selectedSortOption,
setSelectedSortOption,
sortOptions,
};
};
export default useSorting;

+ 1
- 0
src/layouts/MainLayout/MainLayout.styled.js Vedi File

@@ -10,6 +10,7 @@ export const MainLayoutContainer = styled(Container)`
display: flex;
flex: 1;
height: 100%;
margin-top: 80px;
`

export const LeftCard = styled(Grid)`

+ 1
- 0
src/layouts/ProfileLayout/ProfileLayout.styled.js Vedi File

@@ -10,6 +10,7 @@ export const ProfileLayoutContainer = styled(Container)`
max-width: none;
flex: 1;
height: 100%;
margin-top: 80px;
`

export const LeftCard = styled(Grid)`

+ 7
- 4
src/pages/LoginPage/LoginPage.js Vedi File

@@ -8,9 +8,9 @@ import * as Yup from "yup";
import { useTranslation } from "react-i18next";
import {
clearLoginErrors,
fetchUser,
fetchLogin,
} from "../../store/actions/login/loginActions";
import { selectLoginError } from "../../store/selectors/loginSelectors";
import { selectLoginError, selectUserId } from "../../store/selectors/loginSelectors";
import { FORGOT_PASSWORD_PAGE, HOME_PAGE } from "../../constants/pages";
import { ReactComponent as VisibilityOn } from "../../assets/images/svg/eye-striked.svg";
import { ReactComponent as VisibilityOff } from "../../assets/images/svg/eye.svg";
@@ -34,11 +34,13 @@ import {
import selectedTheme from "../../themes";
import loginValidation from "../../validations/loginValidation";
import loginInitialValues from "../../initialValues/loginInitialValues";
import { fetchProfile } from "../../store/actions/profile/profileActions";

const LoginPage = ({ history }) => {
const dispatch = useDispatch();
const { t } = useTranslation();
const error = useSelector(selectLoginError);
const userId = useSelector(selectUserId);

const [showPassword, setShowPassword] = useState(false);
const handleClickShowPassword = () => setShowPassword(!showPassword);
@@ -64,6 +66,7 @@ const LoginPage = ({ history }) => {
}, []);

const handleApiResponseSuccess = () => {
dispatch(fetchProfile(userId))
history.push({
pathname: HOME_PAGE,
state: {
@@ -73,10 +76,10 @@ const LoginPage = ({ history }) => {
};

const handleSubmit = (values) => {
const { email, password: password } = values;
const { email, password } = values;
dispatch(clearLoginErrors());
dispatch(
fetchUser({
fetchLogin({
email,
password,
handleApiResponseSuccess,

+ 1
- 0
src/request/apiEndpoints.js Vedi File

@@ -118,6 +118,7 @@ export default {
'/users?fp={fp}&offer={offer}&landingPageUrl={landingPageUrl}&registrationFlowType={registrationFlowType}',
updateUserRegistration: '/users/{userUid}',
invite: '/users/invite',
getProfile: 'users/'
},
applications: {
application: '/applications/{applicationUid}',

+ 5
- 0
src/request/chatRequest.js Vedi File

@@ -0,0 +1,5 @@
import { getRequest } from "."

export const attemptFetchChats = (payload) => {
return getRequest(`users/${payload}/chat`);
}

+ 0
- 1
src/request/index.js Vedi File

@@ -13,7 +13,6 @@ const request = axios.create({
});

export const getRequest = (url, params = null, options = null) => {
console.log('url: ', url);
return request.get(url, { params, ...options });
}


+ 5
- 0
src/request/profileRequest.js Vedi File

@@ -0,0 +1,5 @@
import { getRequest } from ".";
import apiEndpoints from "./apiEndpoints";

export const attemptFetchProfile = (payload) =>
getRequest(apiEndpoints.users.getProfile + payload);

+ 7
- 0
src/store/actions/chat/chatActionConstants.js Vedi File

@@ -0,0 +1,7 @@
import { createFetchType } from "../actionHelpers";

const CHAT_SCOPE = "CHAT_SCOPE";

export const CHAT_FETCH = createFetchType(CHAT_SCOPE);

export const CHAT_SET = "CHAT_SET";

+ 10
- 0
src/store/actions/chat/chatActions.js Vedi File

@@ -0,0 +1,10 @@
import { CHAT_FETCH, CHAT_SET } from "./chatActionConstants";

export const fetchChats = (payload) => ({
type: CHAT_FETCH,
payload,
})
export const setChats = (payload) => ({
type: CHAT_SET,
payload,
})

+ 1
- 0
src/store/actions/filters/filtersActionConstants.js Vedi File

@@ -8,3 +8,4 @@ export const SET_SUBCATEGORY = "FILTERS_SET_SUBCATEGORY";
export const SET_LOCATIONS = "FILTERS_SET_LOCATIONS";
export const SET_SORT_OPTION = "FILTERS_SET_SORT_OPTION";
export const SET_IS_APPLIED = "FILTERS_SET_IS_APPLIED";
export const SET_QUERY_STRING = "FILTERS_SET_QUERY_STRING";

+ 5
- 1
src/store/actions/filters/filtersActions.js Vedi File

@@ -1,4 +1,4 @@
import { CLEAR_FILTERS, SET_CATEGORY, SET_FILTERS, SET_IS_APPLIED, SET_LOCATIONS, SET_SORT_OPTION, SET_SUBCATEGORY } from "./filtersActionConstants";
import { CLEAR_FILTERS, SET_CATEGORY, SET_FILTERS, SET_IS_APPLIED, SET_LOCATIONS, SET_QUERY_STRING, SET_SORT_OPTION, SET_SUBCATEGORY } from "./filtersActionConstants";

export const setFilters = (payload) => ({
type: SET_FILTERS,
@@ -26,4 +26,8 @@ export const setFilteredSortOption = (payload) => ({
export const setIsAppliedStatus = (payload) => ({
type: SET_IS_APPLIED,
payload,
})
export const setQueryString = (payload) => ({
type: SET_QUERY_STRING,
payload,
})

+ 1
- 1
src/store/actions/login/loginActions.js Vedi File

@@ -14,7 +14,7 @@ import {
} from './loginActionConstants';


export const fetchUser = (payload) => ({
export const fetchLogin = (payload) => ({
type: LOGIN_USER_FETCH,
payload,
});

+ 5
- 1
src/store/actions/offers/offersActionConstants.js Vedi File

@@ -1,6 +1,7 @@
import { createClearType, createErrorType, createFetchType, createSuccessType } from "../actionHelpers";

const OFFERS_SCOPE = "OFFERS_SCOPE";

const OFFERS_MORE_SCOPE = "OFFERS_MORE_SCOPE";
export const OFFERS_FETCH_MORE = createFetchType(OFFERS_MORE_SCOPE);

@@ -9,7 +10,10 @@ export const OFFERS_SUCCESS = createSuccessType(OFFERS_SCOPE);
export const OFFERS_ERROR = createErrorType(OFFERS_SCOPE);
export const OFFERS_CLEAR = createClearType(OFFERS_SCOPE);



export const OFFERS_PINNED_SET = "OFFERS_PINNED_SET";
export const OFFERS_PINNED_ADD = "OFFERS_PINNED_ADD";
export const OFFERS_SET = "OFFERS_SET";
export const OFFERS_ADD = "OFFERS_ADD";
export const OFFER_ADD = "OFFER_ADD";
export const OFFERS_NO_MORE = "OFFERS_NO_MORE";

+ 9
- 5
src/store/actions/offers/offersActions.js Vedi File

@@ -1,4 +1,4 @@
import { OFFERS_ADD, OFFERS_CLEAR, OFFERS_ERROR, OFFERS_FETCH, OFFERS_FETCH_MORE, OFFERS_NO_MORE, OFFERS_SET, OFFERS_SUCCESS, OFFER_ADD } from "./offersActionConstants";
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";

export const fetchOffers = (payload) => ({
type: OFFERS_FETCH,
@@ -19,12 +19,16 @@ export const setOffers = (payload) => ({
type: OFFERS_SET,
payload,
})
export const addOffers = (payload) => ({
type: OFFERS_ADD,
export const setPinnedOffers = (payload) => ({
type: OFFERS_PINNED_SET,
payload
})
export const addOffer = (payload) => ({
type: OFFER_ADD,
export const addPinnedOffers = (payload) => ({
type: OFFERS_PINNED_ADD,
payload,
})
export const addOffers = (payload) => ({
type: OFFERS_ADD,
payload
})
export const fetchMoreOffers = (payload) => ({

+ 8
- 0
src/store/actions/profile/profileActionConstants.js Vedi File

@@ -0,0 +1,8 @@
import { createErrorType, createFetchType, createSuccessType } from "../actionHelpers";

const PROFILE_SCOPE = "PROFILE_SCOPE";
export const PROFILE_FETCH = createFetchType(PROFILE_SCOPE);
export const PROFILE_SUCCESS = createSuccessType(PROFILE_SCOPE);
export const PROFILE_ERROR = createErrorType(PROFILE_SCOPE);

export const PROFILE_SET = "PROFILE_SET";

+ 19
- 0
src/store/actions/profile/profileActions.js Vedi File

@@ -0,0 +1,19 @@
import { PROFILE_ERROR, PROFILE_FETCH, PROFILE_SET, PROFILE_SUCCESS } from "./profileActionConstants";

export const fetchProfile = (payload) => ({
type: PROFILE_FETCH,
payload
})
export const fetchSuccessProfile = (payload) => ({
type: PROFILE_SUCCESS,
payload,
})

export const fetchErrorProfile = (payload) => ({
type: PROFILE_ERROR,
payload,
})
export const setProfile = (payload) => ({
type: PROFILE_SET,
payload,
})

+ 20
- 0
src/store/reducers/chat/chatReducer.js Vedi File

@@ -0,0 +1,20 @@
import { CHAT_SET } from "../../actions/chat/chatActionConstants"
import createReducer from "../../utils/createReducer"

const initialState = {
latestChats: [],
}

export default createReducer(
{
[CHAT_SET]: setChats,
},
initialState
)

function setChats(state, action) {
return {
...state,
latestChats: action.payload
}
}

+ 12
- 0
src/store/reducers/filters/filtersReducer.js Vedi File

@@ -4,6 +4,7 @@ import {
SET_FILTERS,
SET_IS_APPLIED,
SET_LOCATIONS,
SET_QUERY_STRING,
SET_SORT_OPTION,
SET_SUBCATEGORY,
} from "../../actions/filters/filtersActionConstants";
@@ -16,6 +17,7 @@ const initialState = {
locations: [],
sortOption: null,
isApplied: false,
queryString: "",
},
};

@@ -28,6 +30,7 @@ export default createReducer(
[SET_LOCATIONS]: setFilteredLocations,
[SET_SORT_OPTION]: setFilteredSortOption,
[SET_IS_APPLIED]: setIsAppliedStatus,
[SET_QUERY_STRING]: setQueryString,
},
initialState
);
@@ -90,3 +93,12 @@ function setIsAppliedStatus(state, {payload}) {
}
}
}
function setQueryString(state, {payload}) {
return {
...state,
filters: {
...state.filters,
queryString: payload,
}
}
}

+ 14
- 0
src/store/reducers/index.js Vedi File

@@ -10,6 +10,8 @@ import filtersReducer from "./filters/filtersReducer";
import offersReducer from "./offers/offersReducer";
import categoriesReducer from "./categories/categoriesReducer";
import locationsReducer from "./locations/locationsReducer";
import profileReducer from "./profile/profileReducer";
import chatReducer from "./chat/chatReducer";

const loginPersistConfig = {
key: "login",
@@ -50,6 +52,16 @@ const locationsPersistConfig = {
storage: storage,
transform: [createFilter("locations", ["_id", "city"])],
};
const profilePersistConfig = {
key: "profile",
storage: storage,
transform: [createFilter("profile", ["profile"])],
};
const chatPersistConfig = {
key: "chat",
storage: storage,
transform: [createFilter("chat", ["latestChats"])]
}

export default combineReducers({
login: persistReducer(loginPersistConfig, loginReducer),
@@ -60,4 +72,6 @@ export default combineReducers({
offers: offersReducer,
categories: persistReducer(categoriesPersistConfig, categoriesReducer),
locations: persistReducer(locationsPersistConfig, locationsReducer),
profile: persistReducer(profilePersistConfig, profileReducer),
chat: persistReducer(chatPersistConfig, chatReducer)
});

+ 14
- 5
src/store/reducers/offers/offersReducer.js Vedi File

@@ -3,13 +3,15 @@ import {
OFFERS_CLEAR,
OFFERS_ERROR,
OFFERS_NO_MORE,
OFFERS_PINNED_ADD,
OFFERS_PINNED_SET,
OFFERS_SET,
OFFER_ADD,
} from "../../actions/offers/offersActionConstants";
import createReducer from "../../utils/createReducer";

const initialState = {
offers: [],
pinnedOffers: [],
error: "",
newOffer: "",
noMoreOffers: false,
@@ -21,8 +23,9 @@ export default createReducer(
[OFFERS_CLEAR]: clearOffers,
[OFFERS_SET]: setOffers,
[OFFERS_ADD]: addOffers,
[OFFER_ADD]: addOffer,
[OFFERS_NO_MORE]: setNoMoreOffersStatus
[OFFERS_NO_MORE]: setNoMoreOffersStatus,
[OFFERS_PINNED_ADD]: addPinnedOffers,
[OFFERS_PINNED_SET]: setPinnedOffers,
},
initialState
);
@@ -46,10 +49,16 @@ function addOffers(state, action) {
offers: [...state.offers, ...action.payload]
}
}
function addOffer(state, action) {
function setPinnedOffers(state, action) {
return {
...state,
offer: action.payload
pinnedOffers: [...action.payload]
}
}
function addPinnedOffers(state, action) {
return {
...state,
pinnedOffers: [...state.pinnedOffers, ...action.payload]
}
}
function setNoMoreOffersStatus(state, action) {

+ 20
- 0
src/store/reducers/profile/profileReducer.js Vedi File

@@ -0,0 +1,20 @@
import { PROFILE_SET } from "../../actions/profile/profileActionConstants";
import createReducer from "../../utils/createReducer";

const initialState = {
profile: {},
};

export default createReducer(
{
[PROFILE_SET]: setProfile,
},
initialState
);

function setProfile(state, action) {
return {
...state,
profile: action.payload,
};
}

+ 20
- 0
src/store/saga/chatSaga.js Vedi File

@@ -0,0 +1,20 @@
import { all, takeLatest, call, put } from "@redux-saga/core/effects";
import { attemptFetchChats } from "../../request/chatRequest";
import { CHAT_FETCH } from "../actions/chat/chatActionConstants";
import { setChats } from "../actions/chat/chatActions";

function* fetchChats(payload) {
try {
const data = yield call(attemptFetchChats, payload.payload);
console.log(data.data);
yield put(setChats(data.data));
} catch(e) {
console.log(e);
}
}

export default function* chatSaga() {
yield all([
takeLatest(CHAT_FETCH, fetchChats)
]);
}

+ 0
- 1
src/store/saga/forgotPasswordSaga.js Vedi File

@@ -4,7 +4,6 @@ import { FORGOT_PASSWORD, RESET_PASSWORD } from "../actions/user/userActionConst

function* forgotPassword({payload}) {
try {
console.log(payload)
const data = yield call(forgotPasswordRequest, payload.email);
if (data) {
if (payload.handleResponseSuccess) {

+ 5
- 1
src/store/saga/index.js Vedi File

@@ -1,9 +1,11 @@
import { all } from 'redux-saga/effects';
import categoriesSaga from './categoriesSaga';
import chatSaga from './chatSaga';
import forgotPasswordSaga from './forgotPasswordSaga';
import locationsSaga from './locationsSaga';
import loginSaga from './loginSaga';
import offersSaga from './offersSaga';
import profileSaga from './profileSaga';
import registerSaga from './registerSaga';

export default function* rootSaga() {
@@ -13,6 +15,8 @@ export default function* rootSaga() {
forgotPasswordSaga(),
offersSaga(),
categoriesSaga(),
locationsSaga()
locationsSaga(),
profileSaga(),
chatSaga()
]);
}

+ 0
- 1
src/store/saga/locationsSaga.js Vedi File

@@ -5,7 +5,6 @@ import { setLocations } from "../actions/locations/locationsActions";

function* fetchLocations() {
const {data} = yield call(attemptFetchLocations)
console.log(data);
yield put(setLocations(data));
}


+ 4
- 4
src/store/saga/loginSaga.js Vedi File

@@ -33,7 +33,7 @@ import { rejectErrorCodeHelper } from "../../util/helpers/rejectErrorCodeHelper"
import { setUserAccessToken } from "../actions/user/userActions";
import i18next from "i18next";

function* fetchUser({ payload }) {
function* fetchLogin({ payload }) {
try {
const { data } = yield call(attemptLogin, payload);
if (data.token) {
@@ -95,7 +95,7 @@ function* authenticateUser() {
}
}

function* logoutUser() {
function* logout() {
try {
const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN);
const user = jwt.decode(JwtToken);
@@ -125,9 +125,9 @@ function* refreshUserToken({payload}) {

export default function* loginSaga() {
yield all([
takeLatest(LOGIN_USER_FETCH, fetchUser),
takeLatest(LOGIN_USER_FETCH, fetchLogin),
takeLatest(AUTHENTICATE_USER, authenticateUser),
takeLatest(LOGOUT_USER, logoutUser),
takeLatest(LOGOUT_USER, logout),
takeLatest(REFRESH_TOKEN, refreshUserToken)
]);
}

+ 29
- 17
src/store/saga/offersSaga.js Vedi File

@@ -1,19 +1,20 @@
import { all, takeLatest, call, put } from "@redux-saga/core/effects";
import {
attemptAddOffer,
attemptFetchMoreOffers,
attemptFetchOffers,
} from "../../request/offersRequest";
import { setQueryString } from "../actions/filters/filtersActions";
import {
OFFERS_FETCH,
OFFERS_FETCH_MORE,
OFFER_ADD,
} from "../actions/offers/offersActionConstants";
import {
addOffers,
addPinnedOffers,
clearOffers,
setNoMoreOffersStatus,
setOffers,
setPinnedOffers,
} from "../actions/offers/offersActions";

function* fetchOffers(payload) {
@@ -21,12 +22,23 @@ function* fetchOffers(payload) {
yield put(clearOffers());
yield put(setNoMoreOffersStatus(false));
const data = yield call(attemptFetchOffers, payload.payload.queryString);
if (data.data.items.length < 10) {
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));
}
yield put(setOffers(data.data.items));
if (payload.payload.queryString) {
yield put(setQueryString(payload.payload.queryString));
}
yield put(setOffers(data.data.items.regularOffers));
yield put(setPinnedOffers(data.data.items.pinnedOffers));
} catch (e) {
console.log(e);
yield call(console.log, e);
}
}
function* fetchMoreOffers(payload) {
@@ -36,8 +48,18 @@ function* fetchMoreOffers(payload) {
payload.payload?.page,
payload.payload?.queryString
);
yield put(addOffers(data.data.items));
if (data.data.items.length < 10) {
if (payload.payload.queryString) {
yield put(setQueryString(payload.payload.queryString));
}
console.log(data.data.items);
yield put(addOffers(data.data.items.regularOffers));
yield put(addPinnedOffers(data.data.items.pinnedOffers));
if (
data.data.items.pinnedOffers + data.data.items.regularOffers < 10 ||
data.data.items.pinnedOffers.length +
data.data.items.regularOffers.length >
data.data.total
) {
yield put(setNoMoreOffersStatus(true));
}
} catch (e) {
@@ -45,19 +67,9 @@ function* fetchMoreOffers(payload) {
}
}

function* createOffer(payload) {
try {
const data = yield call(attemptAddOffer, payload);
console.log(data);
} catch (e) {
console.log(e);
}
}

export default function* offersSaga() {
yield all([
takeLatest(OFFERS_FETCH, fetchOffers),
takeLatest(OFFERS_FETCH_MORE, fetchMoreOffers),
takeLatest(OFFER_ADD, createOffer),
]);
}

+ 22
- 0
src/store/saga/profileSaga.js Vedi File

@@ -0,0 +1,22 @@
import { all, call, put, takeLatest } from "@redux-saga/core/effects";
import { attemptFetchProfile } from "../../request/profileRequest";
import { PROFILE_FETCH } from "../actions/profile/profileActionConstants";
import { setProfile } from "../actions/profile/profileActions";

function* fetchProfile(payload) {
try {
console.log(payload);
const data = yield call(attemptFetchProfile, payload.payload);
console.log(data.data);
if (data) yield put(setProfile(data.data));
}
catch(e) {
console.log(e);
}
}

export default function* profileSaga() {
yield all([
takeLatest(PROFILE_FETCH, fetchProfile)
])
}

+ 8
- 0
src/store/selectors/chatSelectors.js Vedi File

@@ -0,0 +1,8 @@
import { createSelector } from "reselect";

const chatSelector = (state) => state.chat;

export const selectLatestChats = createSelector(
chatSelector,
(state) => state.latestChats
)

+ 4
- 0
src/store/selectors/filtersSelectors.js Vedi File

@@ -26,3 +26,7 @@ export const selectAppliedStatus = createSelector(
filtersSelector,
(state) => state.filters.isApplied
)
export const selectQueryString = createSelector(
filtersSelector,
(state) => state.filters.queryString
)

+ 4
- 2
src/store/selectors/loginSelectors.js Vedi File

@@ -21,8 +21,10 @@ export const selectJWTToken = createSelector(
loginSelector,
(state) => state.token,
);


export const selectUserId = createSelector(
loginSelector,
(state) => state.token.userId
)
export const selectLoginError = createSelector(
loginSelector,
(state) => state.errorMessage,

+ 4
- 0
src/store/selectors/offersSelectors.js Vedi File

@@ -15,3 +15,7 @@ export const selectNoMoreOffers = createSelector(
offersSelector,
(state) => state.noMoreOffers
)
export const selectPinnedOffers = createSelector(
offersSelector,
(state) => state.pinnedOffers
)

+ 12
- 0
src/store/selectors/profileSelectors.js Vedi File

@@ -0,0 +1,12 @@
import { createSelector } from "reselect";

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

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

+ 2
- 0
src/util/helpers/authScopeHelpers.js Vedi File

@@ -10,8 +10,10 @@ export function authScopeGetHelper(key) {

export function authScopeStringGetHelper(key) {
if (sessionStorage.getItem(SESSION_STORAGE_SCOPE)) {
console.log(sessionStorage.getItem(key))
return sessionStorage.getItem(key);
}
console.log(localStorage.getItem(key))

return localStorage.getItem(key);
}

+ 19
- 0
src/util/helpers/queryHelpers.js Vedi File

@@ -0,0 +1,19 @@
import { sortEnum } from "../../enums/sortEnum";

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);
}
}
if (queryObject.has('_des_popular')) {
queryObjectToReturn.delete('_des_popular');
queryObjectToReturn.append('sortBy', sortEnum.POPULAR.queryString);
}
return queryObjectToReturn.toString();
}

Loading…
Annulla
Salva