Browse Source

Merge branch 'master' of http://176.104.105.124:3000/selenaaasi/trampa-frontend into feature/563

feature/587
jovan.cirkovic 3 years ago
parent
commit
2aa6ecbf5d
33 changed files with 498 additions and 161 deletions
  1. 30
    3
      src/components/Cards/FilterCard/Choser/CategoryChoser/CategoryChoser.js
  2. 20
    3
      src/components/Cards/FilterCard/Choser/LocationChoser/LocationChoser.js
  3. 42
    10
      src/components/Cards/FilterCard/Choser/SubcategoryChoser/SubcategoryChoser.js
  4. 13
    5
      src/components/Cards/FilterCard/FilterCard.js
  5. 11
    5
      src/components/Cards/FilterCard/FilterDropdown/Checkbox/CheckboxDropdownList/CheckboxDropdownList.js
  6. 8
    2
      src/components/Cards/FilterCard/FilterDropdown/Checkbox/FilterCheckboxDropdown.js
  7. 0
    1
      src/components/Cards/FilterCard/FilterFooter/FilterFooter.js
  8. 3
    1
      src/components/Cards/FilterCard/FilterHeader/FilterHeader.js
  9. 0
    2
      src/components/Cards/OfferCard/DeleteOffer/DeleteOffer.js
  10. 0
    1
      src/components/Cards/UserReviewsCard/UserReviewsCard.js
  11. 0
    1
      src/components/CreateReview/SecondStep/SecondStepCreateReview.js
  12. 0
    1
      src/components/DirectChat/DirectChatNewMessage/DirectChatNewMessage.js
  13. 9
    8
      src/components/Header/Header.js
  14. 0
    2
      src/components/Login/Login.js
  15. 0
    1
      src/components/Paging/Paging.js
  16. 2
    1
      src/hooks/useOffers/useFilters.js
  17. 20
    6
      src/hooks/useOffers/useOffers.js
  18. 0
    2
      src/pages/RegisterPages/Register/FirstPart/FirstPartOfRegistration.js
  19. 9
    4
      src/pages/RegisterPages/Register/Register.js
  20. 1
    0
      src/request/apiEndpoints.js
  21. 1
    1
      src/request/index.js
  22. 4
    0
      src/request/offersRequest.js
  23. 16
    0
      src/store/actions/offers/offersActionConstants.js
  24. 50
    0
      src/store/actions/offers/offersActions.js
  25. 1
    1
      src/store/middleware/accessTokensMiddleware.js
  26. 0
    1
      src/store/reducers/login/loginReducer.js
  27. 79
    27
      src/store/reducers/offers/offersReducer.js
  28. 76
    4
      src/store/saga/offersSaga.js
  29. 3
    5
      src/store/saga/registerSaga.js
  30. 8
    0
      src/store/selectors/offersSelectors.js
  31. 80
    62
      src/util/helpers/queryHelpers.js
  32. 11
    0
      src/util/helpers/routeHelpers.js
  33. 1
    1
      src/validations/loginValidation.js

+ 30
- 3
src/components/Cards/FilterCard/Choser/CategoryChoser/CategoryChoser.js View File

@@ -1,4 +1,9 @@
import React from "react";
import React, {
forwardRef,
useEffect,
useImperativeHandle,
useState,
} from "react";
import PropTypes from "prop-types";
import FilterRadioDropdown from "../../FilterDropdown/Radio/FilterRadioDropdown";
import { CategoryChosenIcon, CategoryIcon } from "./CategoryChoser.styled";
@@ -9,13 +14,31 @@ const firstCategoryOption = {
value: { _id: 0 },
};

const CategoryChoser = (props) => {
const CategoryChoser = forwardRef((props, ref) => {
const filters = props.filters;
const { t } = useTranslation();
const [isOpened, setIsOpened] = useState(false);
const handleSelectCategory = (category) => {
filters.category.setSelectedCategory(category);
filters.subcategory.setSelectedSubcategory({});
};
useImperativeHandle(ref, () => ({
closeSection: () => {
setIsOpened(false);
},
isOpened: isOpened ? isOpened : false,
}));

useEffect(() => {
if (
!filters.category.selectedCategoryLocally ||
(filters.category.selectedCategoryLocally?._id === 0 && !isOpened)
) {
setIsOpened(false);
} else {
setIsOpened(true);
}
}, [filters.category.selectedCategoryLocally]);
return (
<FilterRadioDropdown
data={[...filters?.category.allCategories]}
@@ -32,12 +55,16 @@ const CategoryChoser = (props) => {
: t("filters.categories.title")
}
searchPlaceholder={t("filters.categories.placeholder")}
open={isOpened}
handleOpen={() => setIsOpened((prevIsOpened) => !prevIsOpened)}
setSelected={handleSelectCategory}
selected={filters.category.selectedCategoryLocally}
firstOption={firstCategoryOption}
/>
);
};
});

CategoryChoser.displayName = "CategoryChoser";

CategoryChoser.propTypes = {
filters: PropTypes.any,

+ 20
- 3
src/components/Cards/FilterCard/Choser/LocationChoser/LocationChoser.js View File

@@ -1,23 +1,40 @@
import React from "react";
import React, { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import PropTypes from "prop-types";
import FilterCheckboxDropdown from "../../FilterDropdown/Checkbox/FilterCheckboxDropdown";
import { LocationIcon } from "./LocationChoser.styled";
import { useTranslation } from "react-i18next";

const LocationChoser = (props) => {
const LocationChoser = forwardRef((props, ref) => {
const { t } = useTranslation();
const [isOpened, setIsOpened] = useState(false);
const filters = props.filters;

useImperativeHandle(ref, () => ({
closeSection: () => {
setIsOpened(false);
},
}));

useEffect(() => {
if (filters.locations.selectedLocationsLocally.length > 0 && !isOpened) {
setIsOpened(true);
}
}, [filters.locations.selectedLocationsLocally])
return (
<FilterCheckboxDropdown
searchPlaceholder={t("filters.location.placeholder")}
data={[...filters.locations.allLocations]}
filters={[...filters.locations.selectedLocationsLocally]}
open={isOpened}
handleOpen={() => setIsOpened((prevIsOpened) => !prevIsOpened)}
icon={<LocationIcon />}
title={t("filters.location.title")}
setItemsSelected={filters.locations.setSelectedLocations}
/>
);
};
});

LocationChoser.displayName = "LocationChoser";

LocationChoser.propTypes = {
filters: PropTypes.any,

+ 42
- 10
src/components/Cards/FilterCard/Choser/SubcategoryChoser/SubcategoryChoser.js View File

@@ -1,4 +1,10 @@
import React, { useEffect, useMemo, useState } from "react";
import React, {
forwardRef,
useEffect,
useImperativeHandle,
useMemo,
useState,
} from "react";
import PropTypes from "prop-types";
import { SubcategoryIcon } from "./SubcategoryChoser.styled";
import FilterRadioDropdown from "../../FilterDropdown/Radio/FilterRadioDropdown";
@@ -9,7 +15,7 @@ import { useTranslation } from "react-i18next";
// value: { _id: 0 },
// };

const SubcategoryChoser = (props) => {
const SubcategoryChoser = forwardRef((props, ref) => {
const filters = props.filters;
const { t } = useTranslation();
const [isOpened, setIsOpened] = useState(false);
@@ -20,13 +26,37 @@ const SubcategoryChoser = (props) => {
);
}, [filters.category.selectedCategoryLocally]);

useEffect(() => {
if (!filters.category.selectedCategoryLocally || filters.category.selectedCategoryLocally?._id === 0) {
useImperativeHandle(ref, () => ({
closeSection: () => {
setIsOpened(false);
setIsDisabled(true);
},
}));

useEffect(() => {
if (props.queryStringHook.isInitiallyLoaded) {
if (
!filters.category.selectedCategoryLocally ||
filters.category.selectedCategoryLocally?._id === 0
) {
setIsOpened(false);
setIsDisabled(true);
} else {
setIsDisabled(false);
setIsOpened(true);
}
} else {
setIsDisabled(false);
setIsOpened(true);
if (
!filters.subcategory.selectedSubcategoryLocally ||
filters.subcategory.selectedSubcategoryLocally?._id === 0
) {
setIsOpened(false);
if (
!filters.category.selectedCategoryLocally ||
filters.category.selectedCategoryLocally?._id === 0
) {
setIsDisabled(true);
}
}
}
}, [filters.category.selectedCategoryLocally]);

@@ -34,8 +64,6 @@ const SubcategoryChoser = (props) => {
setIsOpened((prevState) => !prevState);
};

console.log(filters);

return (
<FilterRadioDropdown
data={subcategories}
@@ -54,10 +82,14 @@ const SubcategoryChoser = (props) => {
firstOption={filters.subcategory.initialOption}
/>
);
};
});

SubcategoryChoser.displayName = "SubcategoryChoser";

SubcategoryChoser.propTypes = {
filters: PropTypes.any,
categoryOpened: PropTypes.bool,
queryStringHook: PropTypes.any,
};

export default SubcategoryChoser;

+ 13
- 5
src/components/Cards/FilterCard/FilterCard.js View File

@@ -1,4 +1,4 @@
import React from "react";
import React, { useRef } from "react";
import PropTypes from "prop-types";
import { ContentContainer, FilterCardContainer } from "./FilterCard.styled";
import HeaderBack from "../../ItemDetails/Header/Header";
@@ -12,6 +12,14 @@ import SkeletonFilterCard from "./Skeleton/SkeletonFilterCard";
const FilterCard = (props) => {
const offers = props.offers;
const filters = offers.filters;
const categoryRef = useRef(null);
const subcategoryRef = useRef(null);
const locationRef = useRef(null);
const closeAllSections = () => {
categoryRef.current.closeSection();
subcategoryRef.current.closeSection();
locationRef.current.closeSection();
}
return (
<FilterCardContainer
filtersOpened={props.filtersOpened}
@@ -25,17 +33,17 @@ const FilterCard = (props) => {
{/* Header title for my offers */}
{props.myOffers && <HeaderBack />}

<FilterHeader filters={filters} />
<FilterHeader filters={offers} closeAllSections={closeAllSections} />

<ContentContainer>
{/* Categories */}
<CategoryChoser filters={filters} />
<CategoryChoser filters={filters} ref={categoryRef} />

{/* Subcategories */}
<SubcategoryChoser filters={filters} />
<SubcategoryChoser filters={filters} queryStringHook={offers.queryStringHook} ref={subcategoryRef} categoryOpened={categoryRef.current?.isOpened} />

{/* Locations */}
<LocationChoser filters={filters} />
<LocationChoser filters={filters} ref={locationRef}/>
</ContentContainer>

<FilterFooter

+ 11
- 5
src/components/Cards/FilterCard/FilterDropdown/Checkbox/CheckboxDropdownList/CheckboxDropdownList.js View File

@@ -1,4 +1,4 @@
import React from "react";
import React, { useState } from "react";
import PropTypes from "prop-types";
import DropdownList from "../../../../../Dropdown/DropdownList/DropdownList";
import selectedTheme from "../../../../../../themes";
@@ -14,9 +14,14 @@ import SearchField from "./SearchField/SearchField";

const CheckboxDropdownList = (props) => {
const data = props.data;
const [isOpened, setIsOpened] = useState(false)
const handleDelete = (item) => {
props.setItemsSelected([...props.filters.filter((p) => p !== item)]);
};
const handleOpen = () => {
setIsOpened((prevState) => !prevState);
if (props.handleOpen) props.handleOpen();
};
return (
<DropdownList
title={props.title}
@@ -33,10 +38,10 @@ const CheckboxDropdownList = (props) => {
toggleIconClosed={<DropdownDown />}
toggleIconOpened={<DropdownUp />}
fullWidth
open={props.isOpened}
setIsOpened={props.setIsOpened}
open={props?.open !== undefined ? props.open : isOpened}
setIsOpened={handleOpen}
toggleIconStyles={{
backgroundColor: props.isOpened
backgroundColor: props.open
? "white"
: selectedTheme.primaryIconBackgroundColor,
}}
@@ -77,8 +82,9 @@ CheckboxDropdownList.propTypes = {
data: PropTypes.any,
searchPlaceholder: PropTypes.string,
toSearch: PropTypes.string,
isOpened: PropTypes.bool,
setIsOpened: PropTypes.func,
open: PropTypes.bool,
handleOpen: PropTypes.func,
};

export default CheckboxDropdownList;

+ 8
- 2
src/components/Cards/FilterCard/FilterDropdown/Checkbox/FilterCheckboxDropdown.js View File

@@ -52,6 +52,10 @@ const FilterCheckboxDropdown = (props) => {
}
}
};
const handleOpen = () => {
setIsOpened((prevState) => !prevState);
if (props.handleOpen) props.handleOpen();
};

return (
<CheckboxDropdownList
@@ -62,8 +66,8 @@ const FilterCheckboxDropdown = (props) => {
icon={props.icon}
data={data}
searchPlaceholder={props.searchPlaceholder}
isOpened={isOpened}
setIsOpened={setIsOpened}
open={props?.open !== undefined ? props.open : isOpened}
handleOpen={handleOpen}
setItemsSelected={props.setItemsSelected}
>
{dataToShow.map((item) => {
@@ -90,6 +94,8 @@ FilterCheckboxDropdown.propTypes = {
searchPlaceholder: PropTypes.string,
setItemsSelected: PropTypes.func,
filters: PropTypes.array,
open: PropTypes.bool,
handleOpen: PropTypes.func,
};
FilterCheckboxDropdown.defaultProps = {
oneValueAllowed: false,

+ 0
- 1
src/components/Cards/FilterCard/FilterFooter/FilterFooter.js View File

@@ -12,7 +12,6 @@ const FilterFooter = (props) => {
const screenDimensions = useScreenDimensions();
const handleFilters = () => {
filters.apply();
if (props.toggleFilters) props.toggleFilters();
};
return (
<FilterFooterContainer responsiveOpen={screenDimensions.width < 600}>

+ 3
- 1
src/components/Cards/FilterCard/FilterHeader/FilterHeader.js View File

@@ -8,7 +8,8 @@ const FilterHeader = (props) => {
const filters = props.filters;
const { t } = useTranslation();
const clearFilters = () => {
filters.clear();
filters.clearFiltersAndApply();
props.closeAllSections();
};
return (
<FilterHeaderContainer>
@@ -23,6 +24,7 @@ const FilterHeader = (props) => {
FilterHeader.propTypes = {
children: PropTypes.node,
filters: PropTypes.any,
closeAllSections: PropTypes.func,
};

export default FilterHeader;

+ 0
- 2
src/components/Cards/OfferCard/DeleteOffer/DeleteOffer.js View File

@@ -41,8 +41,6 @@ const DeleteOffer = (props) => {
dispatch(fetchProfileOffers(userId));
};

console.log(history);

const removeOfferHandler = () => {
dispatch(removeOffer({ offerId, handleApiResponseSuccess }));
props.closeModalHandler();

+ 0
- 1
src/components/Cards/UserReviewsCard/UserReviewsCard.js View File

@@ -21,7 +21,6 @@ import { useTranslation } from "react-i18next";

const UserReviewsCard = (props) => {
const { t } = useTranslation();
console.log(props);

const review = useMemo(() => {
if (props.givingReview) {

+ 0
- 1
src/components/CreateReview/SecondStep/SecondStepCreateReview.js View File

@@ -9,7 +9,6 @@ import { useTranslation } from "react-i18next";
import selectedTheme from "../../../themes";

const SecondStepCreateReview = (props) => {
console.log(props);
const {t} = useTranslation();

const goToNextStep = () => {

+ 0
- 1
src/components/DirectChat/DirectChatNewMessage/DirectChatNewMessage.js View File

@@ -26,7 +26,6 @@ const DirectChatNewMessage = (props) => {
props.refreshChat();
};
const handleSend = useCallback(() => {
console.log(typedValue);
if (location.state?.offerId) {
initiateNewChat(typedValue);
} else {

+ 9
- 8
src/components/Header/Header.js View File

@@ -53,6 +53,7 @@ import { fetchMineProfile } from "../../store/actions/profile/profileActions";
import CreateOffer from "../Cards/CreateOfferCard/CreateOffer";
import { Drawer as HeaderDrawer } from "./Drawer/Drawer";
import useSearch from "../../hooks/useOffers/useSearch";
import { routeMatches } from "../../util/helpers/routeHelpers";
// import useQueryString from "../../hooks/useOffers/useQueryString";

const Header = () => {
@@ -132,12 +133,12 @@ const Header = () => {
useEffect(() => {
let shouldShowHeader = true;
if (
location.pathname === LOGIN_PAGE ||
location.pathname === REGISTER_PAGE ||
location.pathname === REGISTER_SUCCESSFUL_PAGE ||
location.pathname === FORGOT_PASSWORD_PAGE ||
location.pathname === FORGOT_PASSWORD_MAIL_SENT ||
location.pathname === RESET_PASSWORD_PAGE
routeMatches(LOGIN_PAGE) ||
routeMatches(REGISTER_PAGE) ||
routeMatches(REGISTER_SUCCESSFUL_PAGE) ||
routeMatches(FORGOT_PASSWORD_PAGE) ||
routeMatches(FORGOT_PASSWORD_MAIL_SENT) ||
routeMatches(RESET_PASSWORD_PAGE)
) {
shouldShowHeader = false;
}
@@ -173,8 +174,8 @@ const Header = () => {
};
const handleSearch = (value) => {
if (
history.location.pathname !== HOME_PAGE &&
history.location.pathname !== BASE_PAGE
!routeMatches(HOME_PAGE) &&
!routeMatches(BASE_PAGE)
) {
const newQueryString = new URLSearchParams({ search: value });
history.push({

+ 0
- 2
src/components/Login/Login.js View File

@@ -54,9 +54,7 @@ const Login = () => {

const handleSubmitForm = (e) => {
e.preventDefault();
console.log("handle submit form");
if (!formik.isValid) {
console.log("greska");
formik.setFieldValue("password", "");
}
formik.handleSubmit(e);

+ 0
- 1
src/components/Paging/Paging.js View File

@@ -17,7 +17,6 @@ const Paging = (props) => {
: 1;

let moving = 0;
console.log(props.current)
// Making array of pages which contains 2 pages before and after current page
const pagesAsArray = Array.apply(null, Array(5)).map(() => {});


+ 2
- 1
src/hooks/useOffers/useFilters.js View File

@@ -26,10 +26,11 @@ const useFilters = (clearAll = false) => {
locations.selectedLocationsLocally,
]);

const apply = () => {
const apply = (immediatelyApply = false, applyAllFilters) => {
category.apply();
subcategory.apply();
locations.apply();
if (immediatelyApply) applyAllFilters();
};

const clear = () => {

+ 20
- 6
src/hooks/useOffers/useOffers.js View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo } from "react";
import { useEffect, useMemo, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
KEY_CATEGORY,
@@ -12,6 +12,7 @@ import { fetchCategories } from "../../store/actions/categories/categoriesAction
import { fetchLocations } from "../../store/actions/locations/locationsActions";
import {
selectOffers,
selectPinnedOffers,
selectTotalOffers,
} from "../../store/selectors/offersSelectors";
import useFilters from "./useFilters";
@@ -35,9 +36,11 @@ const useOffers = () => {
const dispatch = useDispatch();
const filters = useFilters();
const queryStringHook = useQueryString();
const pinnedOffers = useSelector(selectPinnedOffers);
const offers = useSelector(selectOffers);
const totalOffers = useSelector(selectTotalOffers);
const history = useHistory();
const [filtersCleared, setFiltersCleared] = useState(false);

// Always fetch categories and locations,
// becouse count of total offers change over time
@@ -48,12 +51,10 @@ const useOffers = () => {
}, []);

useEffect(() => {
console.log('location: ', history.location)
if (history.location.state?.logo) {
console.log('logo');
clear();
}
}, [history.location])
}, [history.location]);

// On every change of query string, new header string should be created
// Header string is shown on Home page above offers
@@ -97,8 +98,15 @@ const useOffers = () => {
}, [queryStringHook.isInitiallyLoaded]);

const allOffersToShow = useMemo(() => {
return offers;
}, [offers]);
return [...pinnedOffers, ...offers];
}, [offers, pinnedOffers]);

useEffect(() => {
if (filtersCleared) {
setFiltersCleared(false);
apply();
}
}, [filtersCleared]);

const apply = () => {
filters.apply();
@@ -111,6 +119,11 @@ const useOffers = () => {
dispatch(setQueryString(convertQueryStringForBackend(newQueryString)));
};

const clearFiltersAndApply = () => {
clear();
setFiltersCleared(true);
};

// Those hooks are below becouse function apply cannot be put on props before initialization
const sorting = useSorting(apply);
const paging = usePaging(apply);
@@ -136,6 +149,7 @@ const useOffers = () => {
queryStringHook,
allOffersToShow,
totalOffers,
clearFiltersAndApply,
apply,
clear,
};

+ 0
- 2
src/pages/RegisterPages/Register/FirstPart/FirstPartOfRegistration.js View File

@@ -22,9 +22,7 @@ const FirstPartOfRegistration = (props) => {
const { t } = useTranslation();

useEffect(() => {
console.log(props.informations)
if (props.informations?.mail) {
console.log(props.informations)
formik.setFieldValue("mail", props.informations.mail);
formik.setFieldValue("password", props.informations.password)
}

+ 9
- 4
src/pages/RegisterPages/Register/Register.js View File

@@ -58,13 +58,16 @@ const Register = () => {
} else {
setMailErrorMessage(t("register.emailFormat"));
}
} else if (error?.error?.response?.data?.toString() === "Company with PIB already exists") {
} else if (
error?.error?.response?.data?.toString() ===
"Company with PIB already exists"
) {
setInformations({ mail, password, image });
setCurrentStep(2);
setPIBError(PIB.toString());
setPIBErrorMessage(t("register.PIBTaken"));
} else {
makeErrorToastMessage(t("register.serverError"))
makeErrorToastMessage(t("register.serverError"));
}
};

@@ -75,7 +78,6 @@ const Register = () => {
};

const handleSubmit = (values) => {
console.log(values);
if (currentStep !== 3) {
setCurrentStep((prevState) => prevState + 1);
} else {
@@ -149,7 +151,10 @@ const Register = () => {
/>
)}
{currentStep === 3 && (
<ThirdPartOfRegistration handleSubmit={handleSubmit} informations={informations} />
<ThirdPartOfRegistration
handleSubmit={handleSubmit}
informations={informations}
/>
)}

{imageError && <ErrorMessage>{t("register.imageError")}</ErrorMessage>}

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

@@ -162,6 +162,7 @@ export default {
offers: {
getOneOffer: "offers",
getOffers: "offers",
getFeaturedOffers: "offers/featured",
addOffer: "offers",
categories: "categories",
locations: "locations",

+ 1
- 1
src/request/index.js View File

@@ -4,7 +4,7 @@ import queryString from "qs";
const request = axios.create({
// baseURL: "http://192.168.88.150:3001/",
// baseURL: "http://192.168.88.175:3005/",
baseURL: "https://trampa-api.dilig.net/",
baseURL: "https://trampa-api-test.dilig.net/",

headers: {
"Content-Type": "application/json",

+ 4
- 0
src/request/offersRequest.js View File

@@ -5,6 +5,10 @@ export const attemptFetchOffers = (payload) => {
if (payload) return getRequest(apiEndpoints.offers.getOffers + payload);
return getRequest(apiEndpoints.offers.getOffers);
};
export const attemptFetchFeaturedOffers = (payload) => {
if (payload) return getRequest(apiEndpoints.offers.getFeaturedOffers + payload);
return getRequest(apiEndpoints.offers.getOffers);
}
export const attemptFetchOneOffer = (payload) => {
const url = `${apiEndpoints.offers.getOneOffer}/${payload}`;
return getRequest(url);

+ 16
- 0
src/store/actions/offers/offersActionConstants.js View File

@@ -12,6 +12,17 @@ export const OFFERS_SUCCESS = createSuccessType(OFFERS_SCOPE);
export const OFFERS_ERROR = createErrorType(OFFERS_SCOPE);
export const OFFERS_CLEAR = createClearType(OFFERS_SCOPE);

export const OFFERS_FEATURED_SCOPE = "OFFERS_FEATURED_SCOPE";
export const OFFERS_FEATURED_FETCH = createFetchType(OFFERS_FEATURED_SCOPE);
export const OFFERS_FEATURED_SUCCESS = createSuccessType(OFFERS_FEATURED_SCOPE);
export const OFFERS_FEATURED_ERROR = createErrorType(OFFERS_FEATURED_SCOPE);
export const OFFERS_FEATURED_CLEAR = createClearType(OFFERS_FEATURED_SCOPE);

export const OFFERS_ALL_SCOPE = "OFFERS_ALL_SCOPE";
export const OFFERS_ALL_FETCH = createFetchType(OFFERS_ALL_SCOPE);
export const OFFERS_ALL_SUCCESS = createSuccessType(OFFERS_ALL_SCOPE);
export const OFFERS_ALL_ERROR = createErrorType(OFFERS_ALL_SCOPE);

export const OFFERS_MORE_SCOPE = "OFFERS_MORE_SCOPE";
export const OFFERS_FETCH_MORE = createFetchType(OFFERS_MORE_SCOPE);
export const OFFERS_FETCH_MORE_SUCCESS = createSuccessType(OFFERS_MORE_SCOPE)
@@ -35,12 +46,17 @@ export const ONE_OFFER_ERROR = createErrorType(ONE_OFFER_SCOPE);
export const OFFERS_PINNED_SET = createSetType("OFFERS_PINNED_SET");
export const OFFERS_PINNED_ADD = createSetType("OFFERS_PINNED_ADD");
export const OFFERS_SET = createSetType("OFFERS_SET");
export const OFFERS_FEATURED_SET = createSetType("OFFERS_FEATURED_SET");
export const OFFER_SET = createSetType("OFFER_SET");
export const OFFERS_ADD = createSetType("OFFERS_ADD");
export const OFFERS_NO_MORE = createSetType("OFFERS_NO_MORE");
export const OFFERS_SET_TOTAL = createSetType("OFFERS_SET_TOTAL");
export const OFFERS_PROFILE_SET = createSetType("OFFERS_PROFILE_SET");
export const OFFERS_MINE_SET = createSetType("OFFERS_MY_ADD");
export const OFFER_INDEX_SET = createSetType("OFFER_INDEX_SET");
export const OFFER_INDEX_CLEAR = createClearType("OFFER_INDEX_CLEAR");
export const OFFER_PAGE_SET = createSetType("OFFER_PAGE_SET");
export const OFFER_FEATURED_PAGE_SET = createSetType("OFFER_FEATURED_PAGE_SET");

export const OFFER_ADD_SCOPE = "OFFER_ADD_SCOPE";
export const OFFER_ADD = createFetchType(OFFER_ADD_SCOPE);

+ 50
- 0
src/store/actions/offers/offersActions.js View File

@@ -2,6 +2,11 @@ import {
OFFERS_ADD,
OFFERS_CLEAR,
OFFERS_ERROR,
OFFERS_FEATURED_CLEAR,
OFFERS_FEATURED_ERROR,
OFFERS_FEATURED_FETCH,
OFFERS_FEATURED_SET,
OFFERS_FEATURED_SUCCESS,
OFFERS_FETCH,
OFFERS_FETCH_MORE,
OFFERS_FETCH_MORE_ERROR,
@@ -26,6 +31,8 @@ import {
OFFER_EDIT,
OFFER_EDIT_ERROR,
OFFER_EDIT_SUCCESS,
OFFER_FEATURED_PAGE_SET,
OFFER_PAGE_SET,
OFFER_REMOVE,
OFFER_REMOVE_ERROR,
OFFER_REMOVE_SUCCESS,
@@ -52,6 +59,37 @@ export const clearOffers = () => ({
type: OFFERS_CLEAR,
});

// Fetch offers
export const fetchAllOffers = (payload) => ({
type: OFFERS_FETCH,
payload,
});
export const fetchAllOffersSuccess = (payload) => ({
type: OFFERS_SUCCESS,
payload,
});
export const fetchAllOffersError = (payload) => ({
type: OFFERS_ERROR,
payload,
});

// Fetch featured offers
export const fetchFeaturedOffers = (payload) => ({
type: OFFERS_FEATURED_FETCH,
payload,
});
export const fetchFeaturedOffersSuccess = (payload) => ({
type: OFFERS_FEATURED_SUCCESS,
payload,
});
export const fetchFeaturedOffersError = (payload) => ({
type: OFFERS_FEATURED_ERROR,
payload,
});
export const clearFeaturedOffers = () => ({
type: OFFERS_FEATURED_CLEAR,
});

// Fetch more offers
export const fetchMoreOffers = (payload) => ({
type: OFFERS_FETCH_MORE,
@@ -176,3 +214,15 @@ export const setOffer = (payload) => ({
type: OFFER_SET,
payload,
});
export const setFeaturedOffers = (payload) => ({
type: OFFERS_FEATURED_SET,
payload
})
export const setOfferPage = (payload) => ({
type: OFFER_PAGE_SET,
payload
})
export const setFeaturedOfferPage = (payload) => ({
type: OFFER_FEATURED_PAGE_SET,
payload
})

+ 1
- 1
src/store/middleware/accessTokensMiddleware.js View File

@@ -13,7 +13,7 @@ import { logoutUser, refreshUserToken } from "../actions/login/loginActions";
// import { setUserAccessToken } from "../actions/user/userActions";

//Change URL with .env
const baseURL = "https://trampa-api.dilig.net/";
const baseURL = "https://trampa-api-test.dilig.net/";
// const baseURL = "http://192.168.88.175:3005/";
// const baseURL = "http://192.168.88.175:3005/";


+ 0
- 1
src/store/reducers/login/loginReducer.js View File

@@ -59,7 +59,6 @@ function setError(state, action) {
}

function resetLoginState() {
console.log('reset');
return initialState;
}


+ 79
- 27
src/store/reducers/offers/offersReducer.js View File

@@ -14,6 +14,12 @@ import {
ONE_OFFER_SUCCESS,
OFFERS_SET_TOTAL,
OFFERS_PROFILE_SET,
OFFERS_FEATURED_CLEAR,
OFFERS_FEATURED_SET,
OFFER_INDEX_SET,
OFFER_INDEX_CLEAR,
OFFER_PAGE_SET,
OFFER_FEATURED_PAGE_SET,
} from "../../actions/offers/offersActionConstants";
import createReducer from "../../utils/createReducer";

@@ -25,8 +31,11 @@ const initialState = {
total: 0,
error: "",
newOffer: "",
selectedOffer:"",
selectedOffer: "",
noMoreOffers: false,
offerIndex: null,
offerPage: null,
featuredOfferPage: null,
};

export default createReducer(
@@ -46,6 +55,12 @@ export default createReducer(
[OFFERS_SET_TOTAL]: setTotalOffers,
[OFFERS_MINE_SET]: setMineOffers,
[OFFERS_PROFILE_SET]: setProfileOffers,
[OFFERS_FEATURED_CLEAR]: clearFeaturedOffers,
[OFFERS_FEATURED_SET]: setFeaturedOffers,
[OFFER_INDEX_SET]: setOfferIndex,
[OFFER_INDEX_CLEAR]: clearOfferIndex,
[OFFER_PAGE_SET]: setOfferPage,
[OFFER_FEATURED_PAGE_SET]: setFeaturedOfferPage,
},
initialState
);
@@ -54,8 +69,8 @@ function fetchOffersError(state, action) {
return { ...state, error: action.payload };
}

function clearOffers() {
return initialState;
function clearOffers(state) {
return { ...state, offers: [] };
}
function setOffers(state, action) {
return {
@@ -72,67 +87,104 @@ function setOffer(state, action) {
function addOffers(state, action) {
return {
...state,
offers: [...state.offers, ...action.payload]
}
offers: [...state.offers, ...action.payload],
};
}
function setPinnedOffers(state, action) {
return {
...state,
pinnedOffers: [...action.payload]
}
pinnedOffers: [...action.payload],
};
}
function addOffer(state, action) {
return {
...state,
offer: action.payload
}
offer: action.payload,
};
}

function fetchOneOffer(state,action) {
function fetchOneOffer(state, action) {
return {
...state,
selectedOffer: action.payload
}
selectedOffer: action.payload,
};
}
function fetchOneOfferSuccess( state, action) {
function fetchOneOfferSuccess(state, action) {
return {
...state,
selectedOffer: action.payload
}
selectedOffer: action.payload,
};
}
function fetchOneOfferError (state, action) {
function fetchOneOfferError(state, action) {
return {
...state,
error: action.payload
}
error: action.payload,
};
}
function addPinnedOffers(state, action) {
return {
...state,
pinnedOffers: [...state.pinnedOffers, ...action.payload]
}
pinnedOffers: [...state.pinnedOffers, ...action.payload],
};
}
function setNoMoreOffersStatus(state, action) {
return {
...state,
noMoreOffers: action.payload
}
noMoreOffers: action.payload,
};
}
function setTotalOffers(state, action) {
return {
...state,
total: action.payload
}
total: action.payload,
};
}
function setMineOffers(state, action) {
return {
...state,
mineOffers: action.payload
}
mineOffers: action.payload,
};
}
function setProfileOffers(state, action) {
return {
...state,
profileOffers: action.payload
profileOffers: action.payload,
};
}

function setFeaturedOffers(state, action) {
return {
...state,
pinnedOffers: action.payload
}
}
function clearFeaturedOffers(state) {
return {
...state,
pinnedOffers: []
}
}
function setOfferIndex(state, {payload}) {
return {
...state,
offerIndex: payload
}
}
function clearOfferIndex(state) {
return {
...state,
offerIndex: null,
}
}
function setOfferPage(state, {payload}) {
return {
...state,
offerPage: payload
}
}
function setFeaturedOfferPage(state, {payload}) {
return {
...state,
featuredOfferPage: payload
}
}

+ 76
- 4
src/store/saga/offersSaga.js View File

@@ -1,11 +1,14 @@
import {
attemptAddOffer,
attemptEditOffer,
attemptFetchFeaturedOffers,
attemptFetchOffers,
attemptFetchOneOffer,
attemptRemoveOffer,
} from "../../request/offersRequest";
import {
// OFFERS_ALL_FETCH,
OFFERS_FEATURED_FETCH,
OFFERS_FETCH,
OFFERS_PROFILE_FETCH,
OFFER_ADD,
@@ -32,6 +35,10 @@ import {
fetchProfileOffersError,
removeOfferError,
editOfferError,
clearFeaturedOffers,
// fetchAllOffersSuccess,
// fetchAllOffersError,
// setFeaturedOfferPage,
} from "../actions/offers/offersActions";
import { all, takeLatest, call, put, select } from "@redux-saga/core/effects";
import {
@@ -55,12 +62,59 @@ import {
} from "../actions/offers/offersActions";
import { selectUserId } from "../selectors/loginSelectors";
import {
// selectFeaturedOfferPage,
// selectOfferPage,
selectOffers,
selectPinnedOffers,
selectTotalOffers,
} from "../selectors/offersSelectors";
import history from "../utils/history";
import { NOT_FOUND_PAGE } from "../../constants/pages";
// import { KEY_PAGE } from "../../constants/queryStringConstants";

// function* fetchAllOffers(payload) {
// try {
// yield put(clearOffers());
// yield put(clearFeaturedOffers());
// const queryString = new URLSearchParams(
// convertQueryStringForBackend(payload.payload.queryString)
// );
// const page = queryString.get(KEY_PAGE);
// const offerPage = yield select(selectOfferPage)
// const featuredOfferPage = yield select(selectFeaturedOfferPage);
// const featuredOffers = yield call(
// attemptFetchFeaturedOffers,
// "?" + removePageAndSizeHelper(queryString.toString())
// );
// const newQueryString = new URLSearchParams(
// convertQueryStringForBackend(payload.payload.queryString)
// );
// const regularOffers = yield call(
// attemptFetchOffers,
// "?" + queryString.toString()
// );
// const offerIndex = featuredOffers.data.length - page * 10;
// if (page > Math.floor(featuredOffers.data.length - (page - 1) * 10)) {
// if (featuredOffers.data.length !== 0) {
// yield put(
// setPinnedOffers(
// featuredOffers.data.slice((page - 1) * 10),
// featuredOffers.data.length
// )
// );
// yield put(setOffers(regularOffers.data.items.slice()));
// }
// }
// yield put(
// setTotalOffers(regularOffers.data.total + featuredOffers.data.length)
// );
// yield put(setPinnedOffers(featuredOffers.data.items.pinnedOffers));
// yield put(fetchAllOffersSuccess());
// } catch (e) {
// yield put(fetchAllOffersError());
// yield call(console.log, e);
// }
// }

function* fetchOffers(payload) {
try {
@@ -72,10 +126,27 @@ function* fetchOffers(payload) {
attemptFetchOffers,
"?" + newQueryString.toString()
);
console.log("data::::::::: ", data.data);
yield put(setTotalOffers(data.data.total));
yield put(setOffers(data.data.items.regularOffers));
yield put(setPinnedOffers(data.data.items.pinnedOffers));
yield put(setOffers(data.data.offers.filter(offer => !offer.pinned)));
yield put(setPinnedOffers(data.data.offers.filter(offer => offer.pinned)));
yield put(fetchOffersSuccess());
} catch (e) {
yield put(fetchOffersError());
yield call(console.log, e);
}
}

function* fetchFeaturedOffers(payload) {
try {
yield put(clearFeaturedOffers());
const newQueryString = new URLSearchParams(
convertQueryStringForBackend(payload.payload.queryString)
);
const data = yield call(
attemptFetchFeaturedOffers,
"?" + newQueryString.toString()
);
yield put(setPinnedOffers(data.data));
yield put(fetchOffersSuccess());
} catch (e) {
yield put(fetchOffersError());
@@ -118,7 +189,6 @@ function* fetchMoreOffers(payload) {

function* createOffer(payload) {
try {
console.log(payload.payload);
yield call(attemptAddOffer, payload.payload.values.offerData);
yield put(addOfferSuccess());
if (payload.payload.handleApiResponseSuccess) {
@@ -212,5 +282,7 @@ export default function* offersSaga() {
takeLatest(OFFERS_PROFILE_FETCH, fetchProfileOffers),
takeLatest(OFFER_REMOVE, removeOffer),
takeLatest(OFFER_EDIT, editOffer),
takeLatest(OFFERS_FEATURED_FETCH, fetchFeaturedOffers),
// takeLatest(OFFERS_ALL_FETCH, fetchAllOffers),
]);
}

+ 3
- 5
src/store/saga/registerSaga.js View File

@@ -1,5 +1,4 @@
import { all, takeLatest, call } from "@redux-saga/core/effects";
import { putRequest } from "../../request";
import { all, takeLatest, call, put } from "@redux-saga/core/effects";
import { attemptRegister } from "../../request/registerRequest";
import { REGISTER_USER_FETCH } from "../actions/register/registerActionConstants";
import {
@@ -33,11 +32,10 @@ function* fetchRegisterUser({ payload }) {
if (payload.values.website?.length === 0)
delete requestData.company.contacts.web;
yield call(attemptRegister, requestData);
console.log("jos nije crash");
if (payload.handleResponseSuccess) {
yield call(payload.handleResponseSuccess);
}
yield putRequest(fetchRegisterUserSuccess());
yield put(fetchRegisterUserSuccess());
} catch (e) {
console.log(e);
let type = "server";
@@ -58,7 +56,7 @@ function* fetchRegisterUser({ payload }) {
if (payload.handleResponseError) {
yield call(payload.handleResponseError, error);
}
yield putRequest(fetchRegisterUserError());
yield put(fetchRegisterUserError());
}
}


+ 8
- 0
src/store/selectors/offersSelectors.js View File

@@ -36,3 +36,11 @@ export const selectProfileOffers = createSelector(
offersSelector,
(state) => state.profileOffers
)
export const selectFeaturedOfferPage = createSelector(
offersSelector,
(state) => state.featuredOfferPage
)
export const selectOfferPage = createSelector(
offersSelector,
(state) => state.offerPage
)

+ 80
- 62
src/util/helpers/queryHelpers.js View File

@@ -1,4 +1,8 @@
import { ALL_CATEGORIES, COMMA, SPREAD } from "../../constants/marketplaceHeaderTitle";
import {
ALL_CATEGORIES,
COMMA,
SPREAD,
} from "../../constants/marketplaceHeaderTitle";
import {
initialSize,
KEY_CATEGORY,
@@ -63,8 +67,6 @@ export const combineQueryStrings = (firstQuery, secondQuery) => {
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));
@@ -73,7 +75,6 @@ export const combineQueryStrings = (firstQuery, secondQuery) => {
// console.log("ovde ispod: ", property);
}
});
console.log("thirdQueryObject[property]: ", thirdQueryObject.toString());

return thirdQueryObject.toString();
};
@@ -150,14 +151,12 @@ export const getQueryObjectHelper = (queryString) => {
}
if (queryObject.has(KEY_SUBCATEGORY)) {
newObject[KEY_SUBCATEGORY] = queryObject.get(KEY_SUBCATEGORY);

}
if (queryObject.has(KEY_SEARCH)) {
newObject[KEY_SEARCH] = queryObject.get(KEY_SEARCH);
}
if (queryObject.has(KEY_NAME)) {
newObject[KEY_NAME] = queryObject.get(KEY_NAME);

}
if (queryObject.has(KEY_LOCATION)) {
const arrayOfLocations = queryObject.getAll(KEY_LOCATION);
@@ -186,67 +185,86 @@ export const getQueryObjectHelper = (queryString) => {

export const makeHeaderStringHelper = (filters) => {
let headerStringLocal = ALL_CATEGORIES;
// Adding category to header string
if (filters.category.selectedCategory?.name) {
headerStringLocal = filters.category.selectedCategory?.name;
// Adding subcategories to header string
if (filters.subcategory.selectedSubcategory?.name) {
headerStringLocal += `${SPREAD}${filters.subcategory.selectedSubcategory.name}`;
}
// Adding category to header string
if (filters.category.selectedCategory?.name) {
headerStringLocal = filters.category.selectedCategory?.name;
// Adding subcategories to header string
if (filters.subcategory.selectedSubcategory?.name) {
headerStringLocal += `${SPREAD}${filters.subcategory.selectedSubcategory.name}`;
}
// Adding locations to header string
if (
filters.locations.selectedLocations &&
filters.locations.selectedLocations?.length > 0
) {
headerStringLocal += SPREAD;
}
// Adding locations to header string
if (
filters.locations.selectedLocations &&
filters.locations.selectedLocations?.length > 0
) {
headerStringLocal += SPREAD;

filters.locations.selectedLocations.forEach((location, index) => {
// Checking if item is last
if (index + 1 === filters.locations.selectedLocations.length) {
headerStringLocal += location.city;
} else {
headerStringLocal += location.city + COMMA;
}
});
}
return headerStringLocal;
}
filters.locations.selectedLocations.forEach((location, index) => {
// Checking if item is last
if (index + 1 === filters.locations.selectedLocations.length) {
headerStringLocal += location.city;
} else {
headerStringLocal += location.city + COMMA;
}
});
}
return headerStringLocal;
};
export const makeQueryStringHelper = (filters, paging, search, sorting) => {
const newQueryString = new URLSearchParams();
if (filters.category.selectedCategoryLocally?.name) {
newQueryString.append(
KEY_CATEGORY,
filters.category.selectedCategoryLocally.name
);
}
if (filters.subcategory.selectedSubcategoryLocally?.name) {
newQueryString.append(
KEY_SUBCATEGORY,
filters.subcategory.selectedSubcategoryLocally.name
);
}
if (filters.locations.selectedLocationsLocally?.length > 0) {
filters.locations.selectedLocationsLocally.forEach((location) =>
newQueryString.append(KEY_LOCATION, location?.city)
);
if (filters.category.selectedCategoryLocally?.name) {
newQueryString.append(
KEY_CATEGORY,
filters.category.selectedCategoryLocally.name
);
}
if (filters.subcategory.selectedSubcategoryLocally?.name) {
newQueryString.append(
KEY_SUBCATEGORY,
filters.subcategory.selectedSubcategoryLocally.name
);
}
if (filters.locations.selectedLocationsLocally?.length > 0) {
filters.locations.selectedLocationsLocally.forEach((location) =>
newQueryString.append(KEY_LOCATION, location?.city)
);
}
if (sorting.selectedSortOption?.value) {
if (sorting.selectedSortOption?.value === sortEnum.NEW.value) {
newQueryString.append(KEY_SORTBY, VALUE_SORTBY_NEW);
}
if (sorting.selectedSortOption?.value) {
if (sorting.selectedSortOption?.value === sortEnum.NEW.value) {
newQueryString.append(KEY_SORTBY, VALUE_SORTBY_NEW);
}
if (sorting.selectedSortOption?.value === sortEnum.OLD.value) {
newQueryString.append(KEY_SORTBY, VALUE_SORTBY_OLD);
}
if (sorting.selectedSortOption?.value === sortEnum.POPULAR.value) {
newQueryString.append(KEY_SORTBY, VALUE_SORTBY_POPULAR);
}
if (sorting.selectedSortOption?.value === sortEnum.OLD.value) {
newQueryString.append(KEY_SORTBY, VALUE_SORTBY_OLD);
}
if (paging.currentPage !== 1) {
newQueryString.append(KEY_PAGE, paging.currentPage);
if (sorting.selectedSortOption?.value === sortEnum.POPULAR.value) {
newQueryString.append(KEY_SORTBY, VALUE_SORTBY_POPULAR);
}
newQueryString.append(KEY_SEARCH, search.searchString ?? "");
return newQueryString;
}
if (paging.currentPage !== 1) {
newQueryString.append(KEY_PAGE, paging.currentPage);
}
newQueryString.append(KEY_SEARCH, search.searchString ?? "");
return newQueryString;
};
export const changePageQueryStringHelper = (queryString, newPage) => {
const newQueryString = new URLSearchParams(queryString);
if (newQueryString.has(KEY_PAGE)) {
newQueryString.delete(KEY_PAGE);
}
newQueryString.set(KEY_PAGE, newPage);
return newQueryString.toString();
};
export const changePageQueryObjectHelper = (queryObject, newPage) => {
if (queryObject.has(KEY_PAGE)) {
queryObject.delete(KEY_PAGE);
}
queryObject.set(KEY_PAGE, newPage);
return queryObject;
};
export const removePageAndSizeHelper = (queryString) => {
const newQueryString = new URLSearchParams(queryString);
if (newQueryString.has(KEY_PAGE)) newQueryString.delete(KEY_PAGE);
if (newQueryString.has(KEY_SIZE)) newQueryString.delete(KEY_SIZE);
return newQueryString.toString();
}



+ 11
- 0
src/util/helpers/routeHelpers.js View File

@@ -0,0 +1,11 @@
import history from "../../store/utils/history";

export const routeMatches = (route) => {
if (
history.location.pathname === route ||
history.location.pathname + "/" === route ||
history.location.pathname.slice(0, -1) === route
)
return true;
return false;
};

+ 1
- 1
src/validations/loginValidation.js View File

@@ -2,7 +2,7 @@ import * as Yup from "yup";
import i18n from "../i18n";

export default Yup.object().shape({
email: Yup.string().email(i18n.t("login.emailFormat")).required(i18n.t("login.mailRequired")),
email: Yup.string().email(i18n.t("login.emailFormat")).required(i18n.t("login.emailRequired")),
password: Yup.string()
.required(i18n.t("login.passwordRequired"))
.min(8, i18n.t("login.passwordLength")),

Loading…
Cancel
Save