Browse Source

Finished feature 654

feature/654
djordjemitrovic00 3 years ago
parent
commit
8cad72c61d

+ 11
- 8
src/components/Cards/CategoryCard/CategoryCard.js View File

@@ -2,6 +2,7 @@ import React from "react";
import PropTypes from "prop-types";
import {
CategoryCardContainer,
CategoryCardDetailsContainer,
CategoryCardLeftContainer,
CategoryCardRightContainer,
} from "./CategoryCard.styled";
@@ -18,16 +19,18 @@ const CategoryCard = (props) => {
<CategoryCardContainer>
<CategoryCardLeftContainer>
<CategoryCardName categoryName={props?.category?.name} />
<CategoryDetail
label={t("admin.categories.noOfOffers")}
value={props?.category?.offerCount}
/>
{!props.hideSecondLabel && (
<CategoryCardDetailsContainer>
<CategoryDetail
label={props?.secondLabel}
value={props?.category?.subcategories?.length}
label={t("admin.categories.noOfOffers")}
value={props?.category?.offerCount}
/>
)}
{!props.hideSecondLabel && (
<CategoryDetail
label={props?.secondLabel}
value={props?.category?.subcategories?.length}
/>
)}
</CategoryCardDetailsContainer>
</CategoryCardLeftContainer>
<CategoryCardRightContainer>
<CategoryRemoveButton />

+ 17
- 0
src/components/Cards/CategoryCard/CategoryCard.styled.js View File

@@ -1,5 +1,6 @@
import { Box } from "@mui/material";
import styled from "styled-components";
import selectedTheme from "../../../themes";

export const CategoryCardContainer = styled(Box)`
background: white;
@@ -8,9 +9,15 @@ export const CategoryCardContainer = styled(Box)`
margin: 5px;
margin-top: 13px;
margin-bottom: 13px;
border: 1px solid ${selectedTheme.colors.borderNormal};
border-radius: 4px;
display: flex;
flex-direction: row;
justify-content: space-between;
position: relative;
@media (max-width: 600px) {
height: 102px;
}
`;
export const CategoryCardLeftContainer = styled(Box)`
display: flex;
@@ -20,3 +27,13 @@ export const CategoryCardRightContainer = styled(Box)`
display: flex;
flex-direction: row;
`;
export const CategoryCardDetailsContainer = styled(Box)`
display: flex;
flex-direction: row;
@media (max-width: 600px) {
position: absolute;
bottom: 18px;
left: 0;
max-height: 16px;
}
`;

+ 4
- 0
src/components/Cards/CategoryCard/CategoryCardName/CategoryCardName.styled.js View File

@@ -14,4 +14,8 @@ export const CategoryCardNameText = styled(Typography)`
padding-top: 12px;
color: ${selectedTheme.colors.primaryPurple};
cursor: pointer;
@media (max-width: 600px) {
font-size: 18px;
padding: 0;
}
`;

+ 23
- 1
src/components/Cards/CategoryCard/CategoryDetail/CategoryDetail.styled.js View File

@@ -11,7 +11,16 @@ export const CategoryDetailContainer = styled(Box)`
min-width: 150px;
text-align: center;
border-left: 1px solid ${hexToRGB(selectedTheme.colors.primaryText, 0.13)};

@media (max-width: 600px) {
margin: 0;
min-width: 123px;
&:nth-child(1) {
border-left: 0;
}
&:nth-child(2) {
padding-left: 18px;
}
}
`;
export const CategoryDetailLabel = styled(Typography)`
font-family: ${selectedTheme.fonts.textFont};
@@ -20,6 +29,12 @@ export const CategoryDetailLabel = styled(Typography)`
letter-spacing: 0.01rem;
padding-top: 14px;
padding-right: 3px;
@media (max-width: 600px) {
position: relative;
bottom: 2.5px;
padding-top: 0;
}
`;
export const CategoryDetailValue = styled(Typography)`
font-family: ${selectedTheme.fonts.textFont};
@@ -28,4 +43,11 @@ export const CategoryDetailValue = styled(Typography)`
line-height: 21px;
padding-top: 11px;
color: ${selectedTheme.colors.primaryPurple};
@media (max-width: 600px) {
position: relative;
bottom: 2.5px;
padding: 0;
font-size: 12px;
font-weight: 600;
}
`;

+ 6
- 5
src/components/Cards/CategoryCard/CategoryEditButton/CategoryEditButton.styled.js View File

@@ -14,10 +14,11 @@ export const CategoryEditButtonContainer = styled(IconButton)`
padding-top: 2px;
text-align: center;
@media (max-width: 600px) {
width: 30px;
height: 30px;
top: 16px;
right: 16px;
width: 32px;
height: 32px;
top: 18px;
right: 18px;
margin-right: 0;
padding: 0;
${(props) =>
props.vertical &&
@@ -29,7 +30,7 @@ export const CategoryEditButtonContainer = styled(IconButton)`
height: 16px;
position: relative;
top: -3px;
left: -3.5px;
left: -1.5px;
}
}
`;

+ 5
- 5
src/components/Cards/CategoryCard/CategoryRemoveButton/CategoryRemoveButton.styled.js View File

@@ -14,10 +14,10 @@ export const CategoryRemoveButtonContainer = styled(IconButton)`
padding-top: 2px;
text-align: center;
@media (max-width: 600px) {
width: 30px;
height: 30px;
top: 16px;
right: 16px;
width: 32px;
height: 32px;
top: 18px;
right: 12px;
padding: 0;
${(props) =>
props.vertical &&
@@ -29,7 +29,7 @@ export const CategoryRemoveButtonContainer = styled(IconButton)`
height: 16px;
position: relative;
top: -3px;
left: -3.5px;
left: -1.5px;
}
}
`;

+ 18
- 10
src/components/Header/Header.js View File

@@ -33,6 +33,7 @@ import MyMessagesButton from "./MyMessagesButton/MyMessagesButton";
import UserButton from "./UserButton/UserButton";
import LoginButton from "./LoginButton/LoginButton";
import RegisterButton from "./RegisterButton/RegisterButton";
import useIsMobile from "../../hooks/useIsMobile";

const Header = () => {
const [showCreateOfferModal, setShowCreateOfferModal] = useState(false);
@@ -47,12 +48,13 @@ const Header = () => {
const drawerRef = useRef(null);
const [shouldShow, setShouldShow] = useState(true);
const routeMatch = useRouteMatch();
const { isMobile } = useIsMobile();

// Dont show header on auth routes(login, register, etc.) and admin routes
useEffect(() => {
if (isAuthRoute() || isAdminRoute()) setShouldShow(false);
if (isAuthRoute() || (isAdminRoute && !isMobile)) setShouldShow(false);
else setShouldShow(true);
}, [routeMatch]);
}, [routeMatch, isMobile]);

// Fetch mine profile on loading home page
useEffect(() => {
@@ -76,14 +78,19 @@ const Header = () => {
}
// Handling search when user is on marketplace and when he is not
const handleSearch = (value) => {
if (!routeMatches(HOME_PAGE) && !routeMatches(BASE_PAGE)) {
const newQueryString = new URLSearchParams({ search: value });
history.push({
pathname: HOME_PAGE,
search: newQueryString.toString(),
});
if (isAdminRoute()) {
console.log("admin");
search.setSearchStringManually(value);
} else {
search.searchOffersImmediately(value);
if (!routeMatches(HOME_PAGE) && !routeMatches(BASE_PAGE)) {
const newQueryString = new URLSearchParams({ search: value });
history.push({
pathname: HOME_PAGE,
search: newQueryString.toString(),
});
} else {
search.searchOffersImmediately(value);
}
}
};

@@ -105,7 +112,7 @@ const Header = () => {
};

if (!shouldShow) {
return (<></>)
return <></>;
}

return (
@@ -180,6 +187,7 @@ const Header = () => {
Header.propTypes = {
isGrid: PropTypes.bool,
showModalHandler: PropTypes.func,
manualSearch: PropTypes.func,
};

export default Header;

+ 21
- 6
src/components/MarketPlace/Header/Header.js View File

@@ -147,10 +147,12 @@ const Header = (props) => {
</HeaderTitleText>
</HeaderTitleContainer>
) : (
<ButtonContainer onClick={goBack}>
<ArrowButton side={"left"}></ArrowButton>
<HeaderText>{t("messages.goBack")}</HeaderText>
</ButtonContainer>
!props.hideBackButton && (
<ButtonContainer onClick={goBack}>
<ArrowButton side={"left"}></ArrowButton>
<HeaderText>{t("messages.goBack")}</HeaderText>
</ButtonContainer>
)
)}
</>
)}
@@ -220,9 +222,21 @@ const Header = (props) => {
</HeaderContainer>
{isMobile && (
<PageTitleContainer>
<SwapsIcon />
{props.users ? (
<UserIcon />
) : props.categories ? (
<CategoryIcon />
) : (
<SwapsIcon />
)}
<SwapsTitle>
{props?.myOffers ? t("header.myOffers") : t("offer.offers")}
{props.users
? t("admin.users.headerTitle")
: props.categories
? t("admin.categories.headerTitle")
: props?.myOffers
? t("header.myOffers")
: t("offer.offers")}
</SwapsTitle>
</PageTitleContainer>
)}
@@ -244,6 +258,7 @@ Header.propTypes = {
categories: PropTypes.bool,
hideGrid: PropTypes.bool,
className: PropTypes.string,
hideBackButton: PropTypes.bool,
};
Header.defaultProps = {
isGrid: false,

+ 8
- 1
src/components/MarketPlace/Header/Header.styled.js View File

@@ -199,12 +199,19 @@ export const CategoryIcon = styled(Category)`
position: relative;
top: 4px;
right: 2px;
`
`;
export const PageTitleContainer = styled(Box)`
position: relative;
left: 6px;
margin-top: 36px;
width: 100px;
@media (max-width: 600px) {
& svg {
width: 12px;
height: 12px;
top: 2px;
}
}
`;
export const SwapsIcon = styled(RefreshIcon)`
width: 12px;

+ 2
- 1
src/components/TextFields/SearchField/SearchField.js View File

@@ -28,7 +28,8 @@ const SearchField = (props) => {
const handleSearch = () => {
if (props.isAdmin) {
console.log(searchRef.current.value);
search.searchOffersImmediately(searchRef.current.value);
if (props.handleSearch) props.handleSearch(searchRef.current.value);
else search.searchOffersImmediately(searchRef.current.value);
} else {
props.searchMyOffers(searchRef.current.value);
props.handleSearch();

+ 15
- 4
src/hooks/useOffers/useSearch.js View File

@@ -3,8 +3,14 @@ import { useDispatch, useSelector } from "react-redux";
import { useHistory } from "react-router-dom";
import { BASE_PAGE, HOME_PAGE } from "../../constants/pages";
import { KEY_SEARCH } from "../../constants/queryStringConstants";
import { setSearchString } from "../../store/actions/filters/filtersActions";
import { selectSearchString } from "../../store/selectors/filtersSelectors";
import {
setManualSearchString,
setSearchString,
} from "../../store/actions/filters/filtersActions";
import {
selectManualSearchString,
selectSearchString,
} from "../../store/selectors/filtersSelectors";
import { routeMatches } from "../../util/helpers/routeHelpers";

const useSearch = (applyAllFilters) => {
@@ -13,6 +19,7 @@ const useSearch = (applyAllFilters) => {
const dispatch = useDispatch();
const searchString = useSelector(selectSearchString);
const history = useHistory();
const manualSearchString = useSelector(selectManualSearchString);

// On every global change of search string, new request to backend should be sent
useEffect(() => {
@@ -48,18 +55,20 @@ const useSearch = (applyAllFilters) => {
}, [searchStringLocally]);

const searchOffers = (searchValue) => {
console.log('searchoff')
setIsInitiallyLoaded(true);
setSearchStringLocally(searchValue);
};

const searchOffersImmediately = (searchValue) => {
console.log('searchoffersimm');
setIsInitiallyLoaded(true);
searchOffers(searchValue);
applyAllFilters();
};

const setSearchStringManually = (searchValue) => {
dispatch(setManualSearchString(searchValue));
};

const clear = () => {
setSearchStringLocally("");
};
@@ -67,9 +76,11 @@ const useSearch = (applyAllFilters) => {
return {
searchOffers,
setSearchStringLocally,
setSearchStringManually,
searchOffersImmediately,
searchStringLocally,
searchString,
manualSearchString,
clear,
};
};

+ 29
- 2
src/pages/AdminCategoriesPage/AdminCategoriesPage.js View File

@@ -11,22 +11,49 @@ import {
AdminCategoriesPageContainer,
AdminCategoriesSearchField,
} from "./AdminCategoriesPage.styled";
import { selectManualSearchString } from "../../store/selectors/filtersSelectors";
import { useMemo } from "react";
import { setManualSearchString } from "../../store/actions/filters/filtersActions";

const AdminCategoriesPage = () => {
const { t } = useTranslation();
const dispatch = useDispatch();
const categories = useSelector(selectCategories);
const manualSearchString = useSelector(selectManualSearchString);
useEffect(() => {
dispatch(fetchCategories());
}, []);
const handleSearch = (value) => {
console.log(value);
dispatch(setManualSearchString(value));
};
const categoriesToShow = useMemo(() => {
if (categories) {
console.log(categories);
console.log(manualSearchString);
if (manualSearchString)
return categories.filter((item) =>
item.name.toLowerCase().includes(manualSearchString.toLowerCase())
);
return categories;
}
return [];
}, [categories, manualSearchString]);
return (
<AdminCategoriesPageContainer>
<AdminCategoriesSearchField
isAdmin
handleSearch={handleSearch}
placeholder={t("admin.categories.placeholder")}
/>
<AdminCategoriesHeader myOffers categories hideGrid isAdmin />
{categories.map((category) => (
<AdminCategoriesHeader
myOffers
categories
hideGrid
isAdmin
hideBackButton
/>
{categoriesToShow.map((category) => (
<CategoryCard
key={category._id}
category={category}

+ 22
- 5
src/pages/AdminCategoriesPage/AdminCategoriesPage.styled.js View File

@@ -5,11 +5,28 @@ import SearchField from "../../components/TextFields/SearchField/SearchField";

export const AdminCategoriesPageContainer = styled(Box)`
padding: 60px;
@media (max-width: 600px) {
padding: 18px;
position: relative;
top: 65px;
}
`;
export const AdminCategoriesHeader = styled(Header)`
/* top: 40px; */
top: 0;
`
/* top: 40px; */
top: 0;
@media (max-width: 600px) {
top: -10px;
& div {
margin-top: 0;
}
& div div:nth-child(1) {
top: 22px;
}
}
`;
export const AdminCategoriesSearchField = styled(SearchField)`
top: -15px;
`
top: -15px;
@media (max-width: 600px) {
display: none;
}
`;

+ 5
- 2
src/store/actions/filters/filtersActionConstants.js View File

@@ -1,6 +1,6 @@
import { createClearType, createSetType } from "../actionHelpers"
import { createClearType, createSetType } from "../actionHelpers";

const FILTERS_SCOPE = "FILTERS"
const FILTERS_SCOPE = "FILTERS";
export const SET_FILTERS = createSetType(FILTERS_SCOPE);
export const CLEAR_FILTERS = createClearType(FILTERS_SCOPE);
export const SET_CATEGORY = createSetType("FILTERS_SET_CATEGORY");
@@ -11,3 +11,6 @@ export const SET_IS_APPLIED = createSetType("FILTERS_SET_IS_APPLIED");
export const SET_QUERY_STRING = createSetType("FILTERS_SET_QUERY_STRING");
export const SET_HEADER_STRING = createSetType("FILTERS_SET_HEADER_STRING");
export const SET_SEARCH_STRING = createSetType("FILTERS_SET_SEARCH");
export const SET_MANUAL_SEARCH_STRING = createSetType(
"FILTERS_SET_MANUAL_SEARCH_STRING"
);

+ 46
- 30
src/store/actions/filters/filtersActions.js View File

@@ -1,41 +1,57 @@
import { CLEAR_FILTERS, SET_CATEGORY, SET_FILTERS, SET_HEADER_STRING, SET_IS_APPLIED, SET_LOCATIONS, SET_QUERY_STRING, SET_SEARCH_STRING, SET_SORT_OPTION, SET_SUBCATEGORY } from "./filtersActionConstants";
import {
CLEAR_FILTERS,
SET_CATEGORY,
SET_FILTERS,
SET_HEADER_STRING,
SET_IS_APPLIED,
SET_LOCATIONS,
SET_MANUAL_SEARCH_STRING,
SET_QUERY_STRING,
SET_SEARCH_STRING,
SET_SORT_OPTION,
SET_SUBCATEGORY,
} from "./filtersActionConstants";

export const setFilters = (payload) => ({
type: SET_FILTERS,
payload,
})
type: SET_FILTERS,
payload,
});
export const clearFilters = () => ({
type: CLEAR_FILTERS
})
type: CLEAR_FILTERS,
});
export const setFilteredCategory = (payload) => ({
type: SET_CATEGORY,
payload
})
type: SET_CATEGORY,
payload,
});
export const setFilteredSubcategory = (payload) => ({
type: SET_SUBCATEGORY,
payload
})
type: SET_SUBCATEGORY,
payload,
});
export const setManualSearchString = (payload) => ({
type: SET_MANUAL_SEARCH_STRING,
payload,
});
export const setFilteredLocations = (payload) => ({
type: SET_LOCATIONS,
payload
})
type: SET_LOCATIONS,
payload,
});
export const setFilteredSortOption = (payload) => ({
type: SET_SORT_OPTION,
payload
})
type: SET_SORT_OPTION,
payload,
});
export const setIsAppliedStatus = (payload) => ({
type: SET_IS_APPLIED,
payload,
})
type: SET_IS_APPLIED,
payload,
});
export const setQueryString = (payload) => ({
type: SET_QUERY_STRING,
payload,
})
type: SET_QUERY_STRING,
payload,
});
export const setHeaderString = (payload) => ({
type: SET_HEADER_STRING,
payload
})
type: SET_HEADER_STRING,
payload,
});
export const setSearchString = (payload) => ({
type: SET_SEARCH_STRING,
payload
})
type: SET_SEARCH_STRING,
payload,
});

+ 29
- 16
src/store/reducers/filters/filtersReducer.js View File

@@ -5,6 +5,7 @@ import {
SET_HEADER_STRING,
SET_IS_APPLIED,
SET_LOCATIONS,
SET_MANUAL_SEARCH_STRING,
SET_SEARCH_STRING,
SET_SORT_OPTION,
SET_SUBCATEGORY,
@@ -23,9 +24,10 @@ const initialState = {
categoryString: "",
subcategoryString: "",
locationsString: "",
text: ""
text: "",
},
searchString: ""
searchString: "",
manualSearchString: "",
},
};

@@ -40,10 +42,21 @@ export default createReducer(
[SET_IS_APPLIED]: setIsAppliedStatus,
[SET_HEADER_STRING]: setHeaderString,
[SET_SEARCH_STRING]: setSearchString,
[SET_MANUAL_SEARCH_STRING]: setManualSearchString,
},
initialState
);

function setManualSearchString(state, { payload }) {
return {
...state,
filters: {
...state.filters,
manualSearchString: payload,
},
};
}

function setFilters(state, { payload }) {
return {
...state,
@@ -84,39 +97,39 @@ function setFilteredLocations(state, { payload }) {
},
};
}
function setFilteredSortOption(state, {payload}) {
function setFilteredSortOption(state, { payload }) {
return {
...state,
filters: {
...state.filters,
sortOption: payload,
}
}
},
};
}
function setIsAppliedStatus(state, {payload}) {
function setIsAppliedStatus(state, { payload }) {
return {
...state,
filters: {
...state.filters,
isApplied: payload,
}
}
},
};
}
function setHeaderString(state, {payload}) {
function setHeaderString(state, { payload }) {
return {
...state,
filters: {
...state.filters,
headerString: payload
}
}
headerString: payload,
},
};
}
function setSearchString(state, {payload}) {
function setSearchString(state, { payload }) {
return {
...state,
filters: {
...state.filters,
searchString: payload
}
}
searchString: payload,
},
};
}

+ 15
- 11
src/store/selectors/filtersSelectors.js View File

@@ -1,36 +1,40 @@
import { createSelector } from 'reselect';
import { createSelector } from "reselect";

const filtersSelector = (state) => state.filters;

export const selectFilters = createSelector(
filtersSelector,
(state) => state.filters,
(state) => state.filters
);
export const selectSelectedCategory = createSelector(
filtersSelector,
(state => state.filters.category)
)
(state) => state.filters.category
);
export const selectSelectedSubcategory = createSelector(
filtersSelector,
(state) => state.filters.subcategory
)
);
export const selectSelectedLocations = createSelector(
filtersSelector,
(state) => state.filters.locations
)
);
export const selectSelectedSortOption = createSelector(
filtersSelector,
(state) => state.filters.sortOption
)
);
export const selectAppliedStatus = createSelector(
filtersSelector,
(state) => state.filters.isApplied
)
);
export const selectHeaderString = createSelector(
filtersSelector,
(state) => state.filters.headerString
)
);
export const selectSearchString = createSelector(
filtersSelector,
(state) => state.filters.searchString,
)
(state) => state.filters.searchString
);
export const selectManualSearchString = createSelector(
filtersSelector,
(state) => state.filters.manualSearchString
);

Loading…
Cancel
Save