| @@ -24,6 +24,7 @@ import { | |||
| ABOUT_PAGE, | |||
| ADMIN_HOME_PAGE, | |||
| ADMIN_USERS_PAGE, | |||
| ADMIN_CATEGORIES_PAGE, | |||
| // POLICY_PRIVACY_PAGE, | |||
| } from "./constants/pages"; | |||
| import LoginPage from "./pages/LoginPage/LoginPage"; | |||
| @@ -48,6 +49,7 @@ import AboutPage from "./pages/About/AboutPage"; | |||
| import AuthRoute from "./components/Router/AuthRoute"; | |||
| import AdminHomePage from "./pages/AdminHomePage/AdminHomePage"; | |||
| import AdminUsersPage from "./pages/AdminUsersPage/AdminUsersPage"; | |||
| import AdminCategoriesPage from "./pages/AdminCategoriesPage/AdminCategoriesPage"; | |||
| // import PrivacyPolicyPage from "./pages/PrivacyPolicy/PrivacyPolicyPage"; | |||
| const AppRoutes = () => { | |||
| @@ -58,6 +60,7 @@ const AppRoutes = () => { | |||
| <AuthRoute exact path={ADMIN_LOGIN_PAGE} component={AdminLoginPage} /> | |||
| <Route path={ADMIN_HOME_PAGE} component={AdminHomePage} /> | |||
| <Route path={ADMIN_USERS_PAGE} component={AdminUsersPage} /> | |||
| <Route path={ADMIN_CATEGORIES_PAGE} component={AdminCategoriesPage} /> | |||
| <Route path={NOT_FOUND_PAGE} component={NotFoundPage} /> | |||
| <Route path={ERROR_PAGE} component={ErrorPage} /> | |||
| <AuthRoute | |||
| @@ -0,0 +1,49 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| CategoryCardContainer, | |||
| CategoryCardLeftContainer, | |||
| CategoryCardRightContainer, | |||
| } from "./CategoryCard.styled"; | |||
| import CategoryCardName from "./CategoryCardName/CategoryCardName"; | |||
| import CategoryDetail from "./CategoryDetail/CategoryDetail"; | |||
| import CategoryCheckButton from "./CategoryCheckButton/CategoryCheckButton"; | |||
| import CategoryEditButton from "./CategoryEditButton/CategoryEditButton"; | |||
| import CategoryRemoveButton from "./CategoryRemoveButton/CategoryRemoveButton"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const CategoryCard = (props) => { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <CategoryCardContainer> | |||
| <CategoryCardLeftContainer> | |||
| <CategoryCardName categoryName={props?.category?.name} /> | |||
| <CategoryDetail | |||
| label={t("admin.categories.noOfOffers")} | |||
| value={props?.category?.offerCount} | |||
| /> | |||
| {!props.hideSecondLabel && ( | |||
| <CategoryDetail | |||
| label={props?.secondLabel} | |||
| value={props?.category?.subcategories?.length} | |||
| /> | |||
| )} | |||
| </CategoryCardLeftContainer> | |||
| <CategoryCardRightContainer> | |||
| <CategoryRemoveButton /> | |||
| <CategoryEditButton /> | |||
| {!props.hideCheckButton && <CategoryCheckButton />} | |||
| </CategoryCardRightContainer> | |||
| </CategoryCardContainer> | |||
| ); | |||
| }; | |||
| CategoryCard.propTypes = { | |||
| children: PropTypes.node, | |||
| category: PropTypes.object, | |||
| hideCheckButton: PropTypes.bool, | |||
| secondLabel: PropTypes.string, | |||
| hideSecondLabel: PropTypes.bool, | |||
| }; | |||
| export default CategoryCard; | |||
| @@ -0,0 +1,22 @@ | |||
| import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| export const CategoryCardContainer = styled(Box)` | |||
| background: white; | |||
| height: 84px; | |||
| width: calc(100% - 10px); | |||
| margin: 5px; | |||
| margin-top: 13px; | |||
| margin-bottom: 13px; | |||
| display: flex; | |||
| flex-direction: row; | |||
| justify-content: space-between; | |||
| `; | |||
| export const CategoryCardLeftContainer = styled(Box)` | |||
| display: flex; | |||
| flex-direction: row; | |||
| `; | |||
| export const CategoryCardRightContainer = styled(Box)` | |||
| display: flex; | |||
| flex-direction: row; | |||
| `; | |||
| @@ -0,0 +1,21 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| CategoryCardNameContainer, | |||
| CategoryCardNameText, | |||
| } from "./CategoryCardName.styled"; | |||
| const CategoryCardName = (props) => { | |||
| return ( | |||
| <CategoryCardNameContainer> | |||
| <CategoryCardNameText>{props.categoryName}</CategoryCardNameText> | |||
| </CategoryCardNameContainer> | |||
| ); | |||
| }; | |||
| CategoryCardName.propTypes = { | |||
| children: PropTypes.node, | |||
| categoryName: PropTypes.string, | |||
| }; | |||
| export default CategoryCardName; | |||
| @@ -0,0 +1,17 @@ | |||
| import { Box, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../themes"; | |||
| export const CategoryCardNameContainer = styled(Box)` | |||
| padding: 18px; | |||
| min-width: 234px; | |||
| `; | |||
| export const CategoryCardNameText = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-weight: 700; | |||
| font-size: 16px; | |||
| line-height: 21px; | |||
| padding-top: 12px; | |||
| color: ${selectedTheme.colors.primaryPurple}; | |||
| cursor: pointer; | |||
| `; | |||
| @@ -0,0 +1,25 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { CheckButton } from "./CategoryCheckButton.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const CategoryCheckButton = () => { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <CheckButton | |||
| variant={"outlined"} | |||
| buttoncolor={selectedTheme.colors.primaryPurple} | |||
| textcolor={selectedTheme.colors.primaryPurple} | |||
| style={{ fontWeight: "600" }} | |||
| > | |||
| {t("admin.categories.checkCategory")} | |||
| </CheckButton> | |||
| ); | |||
| }; | |||
| CategoryCheckButton.propTypes = { | |||
| category: PropTypes.any, | |||
| }; | |||
| export default CategoryCheckButton; | |||
| @@ -0,0 +1,17 @@ | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { PrimaryButton } from "../../../Buttons/PrimaryButton/PrimaryButton"; | |||
| export const CheckButton = styled(PrimaryButton)` | |||
| width: 224px; | |||
| height: 48px; | |||
| margin-top: 9px; | |||
| margin-right: 9px; | |||
| & button:hover { | |||
| background-color: ${selectedTheme.colors.primaryPurple} !important; | |||
| color: white !important; | |||
| } | |||
| @media (max-width: 850px) { | |||
| display: none; | |||
| } | |||
| `; | |||
| @@ -0,0 +1,23 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| CategoryDetailContainer, | |||
| CategoryDetailLabel, | |||
| CategoryDetailValue, | |||
| } from "./CategoryDetail.styled"; | |||
| const CategoryDetail = (props) => { | |||
| return ( | |||
| <CategoryDetailContainer> | |||
| <CategoryDetailLabel>{props.label}</CategoryDetailLabel> | |||
| <CategoryDetailValue>{props.value}</CategoryDetailValue> | |||
| </CategoryDetailContainer> | |||
| ); | |||
| }; | |||
| CategoryDetail.propTypes = { | |||
| label: PropTypes.string, | |||
| value: PropTypes.string, | |||
| }; | |||
| export default CategoryDetail; | |||
| @@ -0,0 +1,31 @@ | |||
| import { Box, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { hexToRGB } from "../../../../util/helpers/colorHelper"; | |||
| export const CategoryDetailContainer = styled(Box)` | |||
| /* display: flex; | |||
| flex-direction: row; */ | |||
| margin-top: 20px; | |||
| margin-bottom: 20px; | |||
| min-width: 150px; | |||
| text-align: center; | |||
| border-left: 1px solid ${hexToRGB(selectedTheme.colors.primaryText, 0.13)}; | |||
| `; | |||
| export const CategoryDetailLabel = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| color: ${selectedTheme.colors.primaryText}; | |||
| font-size: 12px; | |||
| letter-spacing: 0.01rem; | |||
| padding-top: 14px; | |||
| padding-right: 3px; | |||
| `; | |||
| export const CategoryDetailValue = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-weight: 700; | |||
| font-size: 16px; | |||
| line-height: 21px; | |||
| padding-top: 11px; | |||
| color: ${selectedTheme.colors.primaryPurple}; | |||
| `; | |||
| @@ -0,0 +1,20 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| CategoryEditButtonContainer, | |||
| EditIcon, | |||
| } from "./CategoryEditButton.styled"; | |||
| const CategoryEditButton = () => { | |||
| return ( | |||
| <CategoryEditButtonContainer> | |||
| <EditIcon /> | |||
| </CategoryEditButtonContainer> | |||
| ); | |||
| }; | |||
| CategoryEditButton.propTypes = { | |||
| category: PropTypes.any, | |||
| }; | |||
| export default CategoryEditButton; | |||
| @@ -0,0 +1,38 @@ | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { IconButton } from "../../../Buttons/IconButton/IconButton"; | |||
| import { ReactComponent as Edit } from "../../../../assets/images/svg/edit.svg"; | |||
| export const CategoryEditButtonContainer = styled(IconButton)` | |||
| width: 40px; | |||
| height: 40px; | |||
| background-color: ${selectedTheme.colors.primaryIconBackgroundColor}; | |||
| border-radius: 100%; | |||
| position: relative; | |||
| top: 22px; | |||
| margin-right: 18px; | |||
| padding-top: 2px; | |||
| text-align: center; | |||
| @media (max-width: 600px) { | |||
| width: 30px; | |||
| height: 30px; | |||
| top: 16px; | |||
| right: 16px; | |||
| padding: 0; | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| display: none; | |||
| `} | |||
| & button svg { | |||
| width: 16px; | |||
| height: 16px; | |||
| position: relative; | |||
| top: -3px; | |||
| left: -3.5px; | |||
| } | |||
| } | |||
| `; | |||
| export const EditIcon = styled(Edit)` | |||
| ` | |||
| @@ -0,0 +1,20 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| CategoryRemoveButtonContainer, | |||
| RemoveIcon, | |||
| } from "./CategoryRemoveButton.styled"; | |||
| const CategoryRemoveButton = () => { | |||
| return ( | |||
| <CategoryRemoveButtonContainer> | |||
| <RemoveIcon /> | |||
| </CategoryRemoveButtonContainer> | |||
| ); | |||
| }; | |||
| CategoryRemoveButton.propTypes = { | |||
| category: PropTypes.any, | |||
| }; | |||
| export default CategoryRemoveButton; | |||
| @@ -0,0 +1,38 @@ | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { IconButton } from "../../../Buttons/IconButton/IconButton"; | |||
| import { ReactComponent as Remove } from "../../../../assets/images/svg/trash.svg"; | |||
| export const CategoryRemoveButtonContainer = styled(IconButton)` | |||
| width: 40px; | |||
| height: 40px; | |||
| background-color: ${selectedTheme.colors.primaryIconBackgroundColor}; | |||
| border-radius: 100%; | |||
| position: relative; | |||
| top: 22px; | |||
| margin-right: 18px; | |||
| padding-top: 2px; | |||
| text-align: center; | |||
| @media (max-width: 600px) { | |||
| width: 30px; | |||
| height: 30px; | |||
| top: 16px; | |||
| right: 16px; | |||
| padding: 0; | |||
| ${(props) => | |||
| props.vertical && | |||
| ` | |||
| display: none; | |||
| `} | |||
| & button svg { | |||
| width: 16px; | |||
| height: 16px; | |||
| position: relative; | |||
| top: -3px; | |||
| left: -3.5px; | |||
| } | |||
| } | |||
| `; | |||
| export const RemoveIcon = styled(Remove)` | |||
| ` | |||
| @@ -28,7 +28,7 @@ const BigProfileCard = (props) => { | |||
| const blockUser = () => {}; | |||
| return ( | |||
| <> | |||
| <ProfileCardContainer> | |||
| <ProfileCardContainer halfwidth={props.halfwidth}> | |||
| <ProfileCardWrapper variant="outlined"> | |||
| <EditButton onClick={() => setEditProfileModal(true)}> | |||
| <EditIcon /> | |||
| @@ -46,6 +46,7 @@ const BigProfileCard = (props) => { | |||
| <ProfileContact profile={props.profile} isAdmin /> | |||
| </ProfileInfoContainer> | |||
| <CheckButton | |||
| halfwidth={props.halfwidth} | |||
| variant={"outlined"} | |||
| buttoncolor={selectedTheme.colors.primaryPurple} | |||
| textcolor={selectedTheme.colors.primaryPurple} | |||
| @@ -70,6 +71,7 @@ const BigProfileCard = (props) => { | |||
| BigProfileCard.propTypes = { | |||
| profile: PropTypes.any, | |||
| halfwidth: PropTypes.bool, | |||
| }; | |||
| export default BigProfileCard; | |||
| @@ -12,7 +12,7 @@ import { PrimaryButton } from "../../../Buttons/PrimaryButton/PrimaryButton"; | |||
| // import { ReactComponent as Location } from "../../../assets/images/svg/location.svg"; | |||
| export const ProfileCardContainer = styled(Box)` | |||
| width: 100%; | |||
| width: ${(props) => (props.halfwidth ? `49%` : `100%`)}; | |||
| box-sizing: border-box; | |||
| max-height: 184px; | |||
| margin-top: 34px; | |||
| @@ -30,7 +30,6 @@ export const EditIcon = styled(Edit)` | |||
| } | |||
| `; | |||
| export const MessageButton = styled(IconButton)` | |||
| width: 40px; | |||
| height: 40px; | |||
| @@ -92,6 +91,7 @@ export const CheckButton = styled(PrimaryButton)` | |||
| position: absolute; | |||
| bottom: 25px; | |||
| right: 9px; | |||
| display: ${(props) => (props.halfwidth ? `none` : `block`)}; | |||
| & button:hover { | |||
| background-color: ${selectedTheme.colors.primaryPurple} !important; | |||
| color: white !important; | |||
| @@ -4,6 +4,7 @@ import { | |||
| ButtonContainer, | |||
| // ButtonContainer, | |||
| CategoryHeaderIcon, | |||
| CategoryIcon, | |||
| HeaderAltLocation, | |||
| HeaderButton, | |||
| HeaderButtons, | |||
| @@ -89,6 +90,7 @@ const Header = (props) => { | |||
| <> | |||
| <SkeletonHeader skeleton={props?.skeleton} myOffers={props?.myOffers} /> | |||
| <HeaderWrapperContainer | |||
| className={props.className} | |||
| skeleton={props?.skeleton} | |||
| isAdmin={props?.isAdmin} | |||
| > | |||
| @@ -129,10 +131,18 @@ const Header = (props) => { | |||
| <> | |||
| {!isMobile ? ( | |||
| <HeaderTitleContainer> | |||
| {props.users ? <UserIcon /> : <SwapsHeaderIcon />} | |||
| {props.users ? ( | |||
| <UserIcon /> | |||
| ) : props.categories ? ( | |||
| <CategoryIcon /> | |||
| ) : ( | |||
| <SwapsHeaderIcon /> | |||
| )} | |||
| <HeaderTitleText> | |||
| {props.users | |||
| ? t("admin.users.headerTitle") | |||
| : props.categories | |||
| ? t("admin.categories.headerTitle") | |||
| : t("header.myOffers")} | |||
| </HeaderTitleText> | |||
| </HeaderTitleContainer> | |||
| @@ -149,33 +159,35 @@ const Header = (props) => { | |||
| {/* ^^^^^^ */} | |||
| <HeaderOptions> | |||
| <HeaderButtons> | |||
| {/* Setting display of offer cards to full width */} | |||
| <HeaderButton | |||
| iconColor={ | |||
| props?.isGrid | |||
| ? selectedTheme.colors.iconStrokeColor | |||
| : selectedTheme.colors.primaryPurple | |||
| } | |||
| onClick={() => props?.setIsGrid(false)} | |||
| > | |||
| <GridLine /> | |||
| </HeaderButton> | |||
| {/* ^^^^^^ */} | |||
| {!props.hideGrid && ( | |||
| <HeaderButtons> | |||
| {/* Setting display of offer cards to full width */} | |||
| <HeaderButton | |||
| iconColor={ | |||
| props?.isGrid | |||
| ? selectedTheme.colors.iconStrokeColor | |||
| : selectedTheme.colors.primaryPurple | |||
| } | |||
| onClick={() => props?.setIsGrid(false)} | |||
| > | |||
| <GridLine /> | |||
| </HeaderButton> | |||
| {/* ^^^^^^ */} | |||
| {/* Setting display of offer cards to half width (Grid) */} | |||
| <HeaderButton | |||
| iconColor={ | |||
| props?.isGrid | |||
| ? selectedTheme.colors.primaryPurple | |||
| : selectedTheme.colors.iconStrokeColor | |||
| } | |||
| onClick={() => props?.setIsGrid(true)} | |||
| > | |||
| <GridSquare /> | |||
| </HeaderButton> | |||
| {/* ^^^^^^ */} | |||
| </HeaderButtons> | |||
| {/* Setting display of offer cards to half width (Grid) */} | |||
| <HeaderButton | |||
| iconColor={ | |||
| props?.isGrid | |||
| ? selectedTheme.colors.primaryPurple | |||
| : selectedTheme.colors.iconStrokeColor | |||
| } | |||
| onClick={() => props?.setIsGrid(true)} | |||
| > | |||
| <GridSquare /> | |||
| </HeaderButton> | |||
| {/* ^^^^^^ */} | |||
| </HeaderButtons> | |||
| )} | |||
| {/* Select option to choose sorting */} | |||
| <HeaderSelect | |||
| @@ -224,12 +236,14 @@ Header.propTypes = { | |||
| setIsGrid: PropTypes.func, | |||
| isGrid: PropTypes.bool, | |||
| offers: PropTypes.any, | |||
| category: PropTypes.string, | |||
| myOffers: PropTypes.bool, | |||
| skeleton: PropTypes.bool, | |||
| sorting: PropTypes.any, | |||
| isAdmin: PropTypes.bool, | |||
| users: PropTypes.bool, | |||
| categories: PropTypes.bool, | |||
| hideGrid: PropTypes.bool, | |||
| className: PropTypes.string, | |||
| }; | |||
| Header.defaultProps = { | |||
| isGrid: false, | |||
| @@ -7,6 +7,7 @@ import Select from "../../Select/Select"; | |||
| import { ReactComponent as Swaps } from "../../../assets/images/svg/swaps.svg"; | |||
| import { ReactComponent as CategoryHeader } from "../../../assets/images/svg/category-header.svg"; | |||
| import { ReactComponent as User } from "../../../assets/images/svg/user.svg"; | |||
| import { ReactComponent as Category } from "../../../assets/images/svg/category.svg"; | |||
| export const HeaderWrapperContainer = styled(Box)` | |||
| display: ${(props) => (props.skeleton ? "none" : "block")}; | |||
| @@ -194,6 +195,11 @@ export const CategoryHeaderIcon = styled(CategoryHeader)` | |||
| top: 1px; | |||
| } | |||
| `; | |||
| export const CategoryIcon = styled(Category)` | |||
| position: relative; | |||
| top: 4px; | |||
| right: 2px; | |||
| ` | |||
| export const PageTitleContainer = styled(Box)` | |||
| position: relative; | |||
| left: 6px; | |||
| @@ -8,14 +8,17 @@ import { selectLatestChats } from "../../../store/selectors/chatSelectors"; | |||
| import { selectUserId } from "../../../store/selectors/loginSelectors"; | |||
| import { startChat } from "../../../util/helpers/chatHelper"; | |||
| import OffersNotFound from "./OffersNotFound"; | |||
| import HeadersMyOffers from "./HeaderMyOffers.js/HeadersMyOffers"; | |||
| // import HeadersMyOffers from "./SearchBar/SearchBar"; | |||
| import SkeletonOfferCard from "../../Cards/OfferCard/SkeletonOfferCard/SkeletonOfferCard"; | |||
| import BigProfileCard from "../../Cards/ProfileCard/BigProfileCard/BigProfileCard"; | |||
| import SearchField from "../../TextFields/SearchField/SearchField"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const Offers = (props) => { | |||
| const chats = useSelector(selectLatestChats); | |||
| const offersRef = useRef(null); | |||
| const userId = useSelector(selectUserId); | |||
| const { t } = useTranslation(); | |||
| const offers = props?.offers; | |||
| // For skeleton screen | |||
| const arrayForMapping = Array.apply(null, Array(4)).map(() => {}); | |||
| @@ -40,12 +43,18 @@ const Offers = (props) => { | |||
| <FilterIcon /> | |||
| </FilterContainer> | |||
| {(props?.myOffers || props?.isAdmin) && ( | |||
| <HeadersMyOffers | |||
| // <HeadersMyOffers | |||
| <SearchField | |||
| searchMyOffers={offers?.search?.searchOffers} | |||
| handleSearch={offers?.apply} | |||
| isAdmin={props?.isAdmin} | |||
| offers={offers} | |||
| isUsers={props.isUsers} | |||
| placeholder={ | |||
| props.isUsers | |||
| ? t("admin.users.searchPlaceholder") | |||
| : t("header.searchOffers") | |||
| } | |||
| /> | |||
| )} | |||
| {offers?.allOffersToShow?.length === 0 ? ( | |||
| @@ -54,7 +63,11 @@ const Offers = (props) => { | |||
| <OffersContainer ref={offersRef}> | |||
| {props.isUsers | |||
| ? props.users?.map((item) => ( | |||
| <BigProfileCard key={item._id} profile={item} /> | |||
| <BigProfileCard | |||
| key={item._id} | |||
| profile={item} | |||
| halfwidth={props?.isGrid} | |||
| /> | |||
| )) | |||
| : offers?.allOffersToShow?.map((item) => { | |||
| return ( | |||
| @@ -102,7 +115,7 @@ Offers.propTypes = { | |||
| Offers.defaultProps = { | |||
| myOffers: false, | |||
| users: [] | |||
| users: [], | |||
| }; | |||
| export default Offers; | |||
| @@ -1,13 +1,11 @@ | |||
| import React, { useCallback, useEffect, useRef } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { EndIcon, SearchIcon, SearchInput } from "./HeadersMyOffers.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import useSearch from "../../../../hooks/useOffers/useSearch"; | |||
| import { EndIcon, SearchIcon, SearchInput } from "./SearchField.styled"; | |||
| import useSearch from "../../../hooks/useOffers/useSearch"; | |||
| const HeadersMyOffers = (props) => { | |||
| const SearchField = (props) => { | |||
| const searchRef = useRef(null); | |||
| const search = useSearch(() => {}); | |||
| const { t } = useTranslation(); | |||
| let listener = useCallback( | |||
| (event) => { | |||
| // Event keycode 13 = ENTER keycode | |||
| @@ -39,7 +37,7 @@ const HeadersMyOffers = (props) => { | |||
| return ( | |||
| <SearchInput | |||
| isAdmin={props.isAdmin} | |||
| fullWidth | |||
| fullWidth={props.fullWidth} | |||
| InputProps={{ | |||
| endAdornment: ( | |||
| <EndIcon size="36px"> | |||
| @@ -47,25 +45,28 @@ const HeadersMyOffers = (props) => { | |||
| </EndIcon> | |||
| ), | |||
| }} | |||
| placeholder={ | |||
| props.isUsers | |||
| ? t("admin.users.searchPlaceholder") | |||
| : t("header.searchOffers") | |||
| } | |||
| placeholder={props.placeholder} | |||
| onFocus={handleFocusSearch} | |||
| onBlur={handleBlurSearch} | |||
| ref={searchRef} | |||
| className={props.className} | |||
| /> | |||
| ); | |||
| }; | |||
| HeadersMyOffers.propTypes = { | |||
| SearchField.propTypes = { | |||
| children: PropTypes.node, | |||
| searchMyOffers: PropTypes.func, | |||
| handleSearch: PropTypes.func, | |||
| isAdmin: PropTypes.bool, | |||
| offers: PropTypes.any, | |||
| isUsers: PropTypes.bool, | |||
| fullWidth: PropTypes.bool, | |||
| className: PropTypes.string, | |||
| placeholder: PropTypes.string, | |||
| }; | |||
| SearchField.defaultProps = { | |||
| fullWidth: true, | |||
| }; | |||
| export default HeadersMyOffers; | |||
| export default SearchField; | |||
| @@ -1,11 +1,10 @@ | |||
| import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import { Icon } from "../../../Icon/Icon"; | |||
| import { ReactComponent as Search } from "../../../../assets/images/svg/magnifying-glass.svg"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { TextField } from "../../../TextFields/TextField/TextField"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { Icon } from "../../Icon/Icon"; | |||
| import { TextField } from "../TextField/TextField"; | |||
| import { ReactComponent as Search } from "../../../assets/images/svg/magnifying-glass.svg"; | |||
| export const HeadersMyOffersContainer = styled(Box)``; | |||
| export const EndIcon = styled(Icon)``; | |||
| export const SearchIcon = styled(Search)` | |||
| position: relative; | |||
| @@ -20,3 +20,4 @@ export const PRICES_PAGE = "/prices"; | |||
| export const POLICY_PRIVACY_PAGE = "/policy"; | |||
| export const ADMIN_HOME_PAGE = "/admin/home"; | |||
| export const ADMIN_USERS_PAGE = "/admin/users"; | |||
| export const ADMIN_CATEGORIES_PAGE = "/admin/categories"; | |||
| @@ -429,5 +429,12 @@ export default { | |||
| searchPlaceholder: "Pretražite korisnike....", | |||
| checkProfile: "Pogledaj profil" | |||
| }, | |||
| categories: { | |||
| checkCategory: "Pogledaj podkategorije", | |||
| noOfOffers: "Broj objava: ", | |||
| noOfSubcategories: "Broj potkategorija: ", | |||
| headerTitle: "Kategorije", | |||
| placeholder: "Pretražite kategorije..." | |||
| } | |||
| }, | |||
| }; | |||
| @@ -0,0 +1,44 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import CategoryCard from "../../components/Cards/CategoryCard/CategoryCard"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { useEffect } from "react"; | |||
| import { fetchCategories } from "../../store/actions/categories/categoriesActions"; | |||
| import { selectCategories } from "../../store/selectors/categoriesSelectors"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { | |||
| AdminCategoriesHeader, | |||
| AdminCategoriesPageContainer, | |||
| AdminCategoriesSearchField, | |||
| } from "./AdminCategoriesPage.styled"; | |||
| const AdminCategoriesPage = () => { | |||
| const { t } = useTranslation(); | |||
| const dispatch = useDispatch(); | |||
| const categories = useSelector(selectCategories); | |||
| useEffect(() => { | |||
| dispatch(fetchCategories()); | |||
| }, []); | |||
| return ( | |||
| <AdminCategoriesPageContainer> | |||
| <AdminCategoriesSearchField | |||
| isAdmin | |||
| placeholder={t("admin.categories.placeholder")} | |||
| /> | |||
| <AdminCategoriesHeader myOffers categories hideGrid isAdmin /> | |||
| {categories.map((category) => ( | |||
| <CategoryCard | |||
| key={category._id} | |||
| category={category} | |||
| secondLabel={t("admin.categories.noOfSubcategories")} | |||
| /> | |||
| ))} | |||
| </AdminCategoriesPageContainer> | |||
| ); | |||
| }; | |||
| AdminCategoriesPage.propTypes = { | |||
| children: PropTypes.node, | |||
| }; | |||
| export default AdminCategoriesPage; | |||
| @@ -0,0 +1,15 @@ | |||
| import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import Header from "../../components/MarketPlace/Header/Header"; | |||
| import SearchField from "../../components/TextFields/SearchField/SearchField"; | |||
| export const AdminCategoriesPageContainer = styled(Box)` | |||
| padding: 60px; | |||
| `; | |||
| export const AdminCategoriesHeader = styled(Header)` | |||
| /* top: 40px; */ | |||
| top: 0; | |||
| ` | |||
| export const AdminCategoriesSearchField = styled(SearchField)` | |||
| top: -15px; | |||
| ` | |||
| @@ -0,0 +1,11 @@ | |||
| export const hexToRGB = (hex, opacity) => { | |||
| var r = parseInt(hex.slice(1, 3), 16), | |||
| g = parseInt(hex.slice(3, 5), 16), | |||
| b = parseInt(hex.slice(5, 7), 16); | |||
| if (opacity) { | |||
| return `rgba(${r}, ${g}, ${b}, ${opacity})`; | |||
| } else { | |||
| return `rgb(${r}, ${g}, ${b})`; | |||
| } | |||
| } | |||
| @@ -1,4 +1,5 @@ | |||
| import { | |||
| ADMIN_CATEGORIES_PAGE, | |||
| ADMIN_HOME_PAGE, | |||
| ADMIN_LOGIN_PAGE, | |||
| ADMIN_USERS_PAGE, | |||
| @@ -46,7 +47,8 @@ export const isAdminRoute = () => { | |||
| if ( | |||
| routeMatches(ADMIN_LOGIN_PAGE) || | |||
| routeMatches(ADMIN_HOME_PAGE) || | |||
| routeMatches(ADMIN_USERS_PAGE) | |||
| routeMatches(ADMIN_USERS_PAGE) || | |||
| routeMatches(ADMIN_CATEGORIES_PAGE) | |||
| ) | |||
| return true; | |||
| return false; | |||