Browse Source

Finished feature 657 and merged with master

feature/657
Djordje Mitrovic 3 years ago
parent
commit
be44196c7c
33 changed files with 564 additions and 91 deletions
  1. 2
    14
      src/AppRoutes.js
  2. 4
    0
      src/assets/images/svg/dollar-sign.svg
  3. 94
    0
      src/components/Admin/Sidebar/Sidebar.js
  4. 125
    0
      src/components/Admin/Sidebar/Sidebar.styled.js
  5. 2
    1
      src/components/Cards/ProfileCard/ProfileStats/ProfileStats.js
  6. 1
    1
      src/components/ItemDetails/ItemDetails.styled.js
  7. 8
    0
      src/components/ProfileMini/ProfileMini.js
  8. 14
    0
      src/components/ProfileMini/ProfileMini.styled.js
  9. 17
    0
      src/components/Router/AdminRoute.js
  10. 1
    1
      src/components/UserReviews/UserReviews.styled.js
  11. 35
    0
      src/constants/adminNavigation.js
  12. 13
    11
      src/constants/pages.js
  13. 9
    0
      src/i18n/resources/rs.js
  14. 4
    3
      src/layouts/MainLayout/MainLayout.js
  15. 0
    0
      src/pages/AdminHomePage/AdminCategoriesPage/AdminCategoriesPage.js
  16. 0
    0
      src/pages/AdminHomePage/AdminCategoriesPage/AdminCategoriesPage.styled.js
  17. 66
    9
      src/pages/AdminHomePage/AdminHomePage.js
  18. 6
    0
      src/pages/AdminHomePage/AdminHomePage.styled.js
  19. 15
    0
      src/pages/AdminHomePage/AdminLocationsPage/AdminLocationsPage.js
  20. 6
    0
      src/pages/AdminHomePage/AdminLocationsPage/AdminLocationsPage.styled.js
  21. 15
    0
      src/pages/AdminHomePage/AdminPaymentPage/AdminPaymentPage.js
  22. 6
    0
      src/pages/AdminHomePage/AdminPaymentPage/AdminPaymentPage.styled.js
  23. 11
    7
      src/pages/AdminHomePage/AdminSubcategoriesPage/AdminSubcategoriesPage.js
  24. 5
    5
      src/pages/AdminHomePage/AdminSubcategoriesPage/AdminSubcategoriesPage.styled.js
  25. 0
    0
      src/pages/AdminHomePage/AdminUsersPage/AdminUsersPage.js
  26. 2
    2
      src/request/index.js
  27. 2
    1
      src/request/loginRequest.js
  28. 10
    11
      src/store/actions/login/loginActionConstants.js
  29. 8
    3
      src/store/actions/login/loginActions.js
  30. 2
    2
      src/store/middleware/accessTokensMiddleware.js
  31. 51
    13
      src/store/saga/loginSaga.js
  32. 4
    0
      src/store/selectors/loginSelectors.js
  33. 26
    7
      src/util/helpers/routeHelpers.js

+ 2
- 14
src/AppRoutes.js View File

@@ -48,10 +48,8 @@ import MyOffers from "./pages/MyOffers/MyOffers";
// import PricesPage from "./pages/Prices/PricesPage";
import AboutPage from "./pages/About/AboutPage";
import AuthRoute from "./components/Router/AuthRoute";
import AdminRoute from "./components/Router/AdminRoute";
import AdminHomePage from "./pages/AdminHomePage/AdminHomePage";
import AdminUsersPage from "./pages/AdminUsersPage/AdminUsersPage";
import AdminCategoriesPage from "./pages/AdminCategoriesPage/AdminCategoriesPage";
import AdminSubcategoriesPage from "./pages/AdminSubcategoriesPage/AdminSubcategoriesPage";
// import PrivacyPolicyPage from "./pages/PrivacyPolicy/PrivacyPolicyPage";

const AppRoutes = () => {
@@ -60,17 +58,7 @@ const AppRoutes = () => {
<Route exact path={BASE_PAGE} component={HomePage} />
<AuthRoute exact path={LOGIN_PAGE} component={LoginPage} />
<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_SUBCATEGORIES_PAGE}
component={AdminSubcategoriesPage}
/>
<Route
exact
path={ADMIN_CATEGORIES_PAGE}
component={AdminCategoriesPage}
/>
<AdminRoute path={ADMIN_HOME_PAGE} component={AdminHomePage} />
<Route path={NOT_FOUND_PAGE} component={NotFoundPage} />
<Route path={ERROR_PAGE} component={ErrorPage} />
<AuthRoute

+ 4
- 0
src/assets/images/svg/dollar-sign.svg View File

@@ -0,0 +1,4 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11 0.916748V21.0834" stroke="#C4C4C4" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15.5833 4.58325H8.70833C7.85743 4.58325 7.04138 4.92127 6.4397 5.52295C5.83802 6.12463 5.5 6.94068 5.5 7.79159C5.5 8.64249 5.83802 9.45854 6.4397 10.0602C7.04138 10.6619 7.85743 10.9999 8.70833 10.9999H13.2917C14.1426 10.9999 14.9586 11.3379 15.5603 11.9396C16.162 12.5413 16.5 13.3573 16.5 14.2083C16.5 15.0592 16.162 15.8752 15.5603 16.4769C14.9586 17.0786 14.1426 17.4166 13.2917 17.4166H5.5" stroke="#C4C4C4" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

+ 94
- 0
src/components/Admin/Sidebar/Sidebar.js View File

@@ -0,0 +1,94 @@
import React from "react";
import {
SidebarContainer,
SidebarContent,
SidebarHeader,
SidebarProfileImageContainer,
SidebarProfileImage,
SidebarProfileName,
SidebarNavigation,
SidebarNavigationMeni,
SidebarNavigationMeniItemUl,
SidebarNavigationMeniItem,
SidebarNavigationMeniItemIcon,
SidebarProfileRole,
SidebarNavigationMeniLogout,
} from "./Sidebar.styled";
import { ReactComponent as LogoHorizontal } from "../../../assets/images/svg/logo-horizontal.svg";
import { useHistory } from "react-router-dom";
import { useSelector } from "react-redux";
import { selectMineProfile } from "../../../store/selectors/profileSelectors";
import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter";
import useIsMobile from "../../../hooks/useIsMobile";
import { ADMIN_NAVIGATION } from "../../../constants/adminNavigation";
import { ReactComponent as Logout } from "../../../assets/images/svg/log-out.svg";
import { useTranslation } from "react-i18next";
import { useDispatch } from "react-redux";
import { logoutAdmin } from "../../../store/actions/login/loginActions";
import { isInRoute, routeMatches } from "../../../util/helpers/routeHelpers";
import { ADMIN_HOME_PAGE, ADMIN_USERS_PAGE } from "../../../constants/pages";

const Sidebar = () => {
const history = useHistory();
const profile = useSelector(selectMineProfile);
const dispatch = useDispatch();
const { isMobile } = useIsMobile();
const { t } = useTranslation();
console.log(profile);
const routeToItem = (route) => {
console.log(route);
history.push(route);
};
const logoutHandler = () => {
dispatch(logoutAdmin());
};

return (
<SidebarContainer>
<SidebarHeader>
<LogoHorizontal />
</SidebarHeader>
<SidebarContent>
<SidebarProfileImageContainer>
<SidebarProfileImage
src={getImageUrl(profile.image, variants.profileImage, isMobile)}
/>
<SidebarProfileName>{profile.company.name}</SidebarProfileName>
<SidebarProfileRole>{t("admin.navigation.role")}</SidebarProfileRole>
</SidebarProfileImageContainer>
<SidebarNavigation>
<SidebarNavigationMeni>
{t("admin.navigation.menu")}
</SidebarNavigationMeni>
<SidebarNavigationMeniItemUl>
{ADMIN_NAVIGATION.map((value) => {
let isRouteActive = isInRoute(value.route);
if (
routeMatches(ADMIN_HOME_PAGE) &&
routeMatches(ADMIN_USERS_PAGE, value.route)
)
isRouteActive = true;
return (
<SidebarNavigationMeniItem
active={isRouteActive}
key={value.text}
onClick={() => routeToItem(value.route)}
>
<SidebarNavigationMeniItemIcon active={isRouteActive}>
{value.icon}
</SidebarNavigationMeniItemIcon>
{value.text}
</SidebarNavigationMeniItem>
);
})}
</SidebarNavigationMeniItemUl>
<SidebarNavigationMeniLogout onClick={logoutHandler}>
<Logout /> {t("admin.navigation.logout")}
</SidebarNavigationMeniLogout>
</SidebarNavigation>
</SidebarContent>
</SidebarContainer>
);
};

export default Sidebar;

+ 125
- 0
src/components/Admin/Sidebar/Sidebar.styled.js View File

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

export const SidebarContainer = styled(Box)`
margin-top: -30px;
position: fixed;
bottom: 0;
left: 0;
min-width: 20%;
overflow-y: auto;
height: 100vh;
`;

export const SidebarHeader = styled(Box)`
background-color: #f5edff;
padding-top: 36px;
padding-left: 36px;
padding-bottom: 36px;
`;

export const SidebarContent = styled(Box)`
background-color: #fff;
`;

export const SidebarProfileImageContainer = styled(Box)`
display: flex;
flex-direction: column;
align-items: center;
padding: 36px 0 120px 0;
`;

export const SidebarProfileImage = styled.img`
width: 108px;
height: 108px;
border-radius: 100%;
`;

export const SidebarProfileName = styled(Typography)`
font-family: "DM Sans";
font-size: 16px;
font-weight: 700;
margin-top: 19px;
color: ${selectedTheme.colors.primaryPurple};
`;

export const SidebarProfileRole = styled(Typography)`
font-family: "DM Sans";
font-size: 12px;
color: ${selectedTheme.colors.primaryPurple};
`;

export const SidebarNavigation = styled(Box)`
margin-left: 18px;
display: flex;
flex-direction: column;
`;

export const SidebarNavigationMeni = styled(Typography)`
font-family: "DM Sans";
font-size: 24px;
font-weight: 700;
margin-bottom: 56px;
margin-left: 18px;
`;

export const SidebarNavigationMeniItemUl = styled.ul``;
export const SidebarNavigationMeniItem = styled.li`
padding-top: 19px;
padding-bottom: 19px;
padding-left: 18px;
font-family: "DM Sans";
font-size: 16px;
font-weight: 400;
display: flex;
align-items: center;
color: ${selectedTheme.colors.primaryPurple};
cursor: pointer;
${(props) =>
props.active &&
`
background-color: #f5edff;
font-weight: 700;
`}
path {
stroke: #c4c4c4;
}

&:hover {
background-color: #f5edff;
font-weight: 700;

path {
stroke: #feb005;
}
}
`;

export const SidebarNavigationMeniItemIcon = styled(Box)`
margin-right: 9px;

${(props) =>
props.active &&
`
path {
stroke: #feb005;
}
`}
`;

export const SidebarNavigationMeniLogout = styled(Box)`
font-family: "DM Sans";
font-size: 16px;
padding-top: 19px;
padding-bottom: 19px;
padding-left: 18px;
margin-top: 100px;
color: ${selectedTheme.colors.primaryPurple};
cursor: pointer;

&:hover {
background-color: #f5edff;
font-weight: 700;
}
`;

+ 2
- 1
src/components/Cards/ProfileCard/ProfileStats/ProfileStats.js View File

@@ -10,7 +10,7 @@ import { useTranslation } from "react-i18next";
const ProfileStats = (props) => {
const { t } = useTranslation();
return (
<ProfileStatsContainer>
<ProfileStatsContainer className={props.className}>
<ProfileStatsGrid>
<StatsItem variant="subtitle2">
<b>{props.profile?.statistics?.publishes?.count}</b>
@@ -39,6 +39,7 @@ const ProfileStats = (props) => {
ProfileStats.propTypes = {
profile: PropTypes.object,
percentOfSucceededExchanges: PropTypes.number,
className: PropTypes.string,
};

export default ProfileStats;

+ 1
- 1
src/components/ItemDetails/ItemDetails.styled.js View File

@@ -12,7 +12,7 @@ export const OfferIconContainer = styled(Box)`

@media screen and (max-width: 600px) {
margin-left: 0;
margin-top: 228px;
margin-top: 310px;

svg {
width: 14px;

+ 8
- 0
src/components/ProfileMini/ProfileMini.js View File

@@ -3,6 +3,7 @@ import {
ProfileHeader,
ProfileHeaderIconContainer,
ProfileHeaderText,
ProfileMiniStats,
} from "./ProfileMini.styled";
import { useSelector } from "react-redux";
import { selectOffer } from "../../store/selectors/offersSelectors";
@@ -25,6 +26,7 @@ const ProfileMini = () => {
if (offer?.offer?.userId?.toString() === userId?.toString()) return true;
return false;
}, [offer, userId]);
console.log(offer);
return (
<>
{isLoadingOfferContent || isLoadingOfferContent === undefined ? (
@@ -40,6 +42,12 @@ const ProfileMini = () => {
isMyProfile={isMyProfile}
singleOffer
/>
<ProfileMiniStats
profile={offer.companyData}
percentOfSucceededExchanges={
offer.companyData?.statistics?.exchanges?.total
}
/>
</ProfileHeader>
)}
</>

+ 14
- 0
src/components/ProfileMini/ProfileMini.styled.js View File

@@ -2,6 +2,7 @@ import styled from "styled-components";
import { Box } from "@mui/system";
import { Typography } from "@mui/material";
import selectedTheme from "../../themes";
import ProfileStats from "../Cards/ProfileCard/ProfileStats/ProfileStats";

export const ProfileHeader = styled(Box)`
margin-top: 60px;
@@ -34,3 +35,16 @@ export const ProfileHeaderText = styled(Typography)`
font-size: 12px;
}
`;
export const ProfileMiniStats = styled(ProfileStats)`
position: relative;
width: 100%;
margin-left: 0;
margin-top: -1rem;
margin-bottom: 36px;
border-radius: 0 0 4px 4px;
border: 1px solid ${selectedTheme.colors.primaryPurple};

@media (max-width: 600px) {
width: 92%;
}
`;

+ 17
- 0
src/components/Router/AdminRoute.js View File

@@ -0,0 +1,17 @@
import React, { useMemo } from "react";
import { Redirect, Route } from "react-router";
import { useSelector } from "react-redux";
import { HOME_PAGE } from "../../constants/pages";
import { selectRoles } from "../../store/selectors/loginSelectors";

const AdminRoute = ({ ...props }) => {
const role = useSelector(selectRoles);
const isUserAdmin = useMemo(() => {
if (!role?.includes("Admin")) return false;
return true;
}, [role]);

return isUserAdmin ? <Route {...props} /> : <Redirect to={HOME_PAGE} />;
};

export default AdminRoute;

+ 1
- 1
src/components/UserReviews/UserReviews.styled.js View File

@@ -65,7 +65,7 @@ export const ReviewList = styled(List)`
border: 1px solid ${selectedTheme.colors.borderNormal};
overflow-y: auto;
overflow-x: hidden;
max-height: ${(props) => (props.isProfileReviews ? "70vh" : "43vh")};
max-height: ${(props) => (props.isProfileReviews ? "70vh" : "39vh")};

/* overflow-y: auto; */
&::-webkit-scrollbar {

+ 35
- 0
src/constants/adminNavigation.js View File

@@ -0,0 +1,35 @@
import React from "react";
import { ReactComponent as UserIcon } from "../assets/images/svg/user-gray.svg";
import { ReactComponent as CategoryIcon } from "../assets/images/svg/category.svg";
import { ReactComponent as LocationIcon } from "../assets/images/svg/location.svg";
import { ReactComponent as DollarIcon } from "../assets/images/svg/dollar-sign.svg";
import {
ADMIN_CATEGORIES_PAGE,
ADMIN_LOCATIONS_PAGE,
ADMIN_PAYMENT_PAGE,
ADMIN_USERS_PAGE,
} from "./pages";
import i18n from "../i18n";

export const ADMIN_NAVIGATION = [
{
text: i18n.t("admin.navigation.users"),
icon: <UserIcon />,
route: `${ADMIN_USERS_PAGE}`,
},
{
text: i18n.t("admin.navigation.categories"),
icon: <CategoryIcon />,
route: `${ADMIN_CATEGORIES_PAGE}`,
},
{
text: i18n.t("admin.navigation.locations"),
icon: <LocationIcon />,
route: `${ADMIN_LOCATIONS_PAGE}`,
},
{
text: i18n.t("admin.navigation.payment"),
icon: <DollarIcon />,
route: `${ADMIN_PAYMENT_PAGE}`,
},
];

+ 13
- 11
src/constants/pages.js View File

@@ -1,24 +1,26 @@
export const BASE_PAGE = '/';
export const LOGIN_PAGE = '/login';
export const FORGOT_PASSWORD_PAGE = '/forgot-password';
export const ADMIN_LOGIN_PAGE = '/admin/login';
export const HOME_PAGE = '/home';
export const ERROR_PAGE = '/error-page';
export const NOT_FOUND_PAGE = '/not-found';
export const FORGOT_PASSWORD_MAIL_SENT = '/forgot-password/mail-sent';
export const BASE_PAGE = "/";
export const LOGIN_PAGE = "/login";
export const FORGOT_PASSWORD_PAGE = "/forgot-password";
export const ADMIN_LOGIN_PAGE = "/admin/login";
export const HOME_PAGE = "/home";
export const ERROR_PAGE = "/error-page";
export const NOT_FOUND_PAGE = "/not-found";
export const FORGOT_PASSWORD_MAIL_SENT = "/forgot-password/mail-sent";
export const REGISTER_PAGE = "/register";
export const REGISTER_SUCCESSFUL_PAGE = "/register/success";
export const RESET_PASSWORD_PAGE = "/reset-password/:token";
export const CREATE_OFFER_PAGE = "/create-offer";
export const ITEM_DETAILS_PAGE = "/proizvodi/:idProizvod";
export const PROFILE_PAGE = "/profile/:idProfile"
export const PROFILE_PAGE = "/profile/:idProfile";
export const CHAT_PAGE = "/messages";
export const CHAT_MESSAGE_PAGE = "/messages/:idChat";
export const MY_OFFERS_PAGE = "/myoffers"
export const MY_OFFERS_PAGE = "/myoffers";
export const ABOUT_PAGE = "/about";
export const PRICES_PAGE = "/prices";
export const POLICY_PRIVACY_PAGE = "/policy";
export const ADMIN_HOME_PAGE = "/admin/home";
export const ADMIN_HOME_PAGE = "/admin";
export const ADMIN_USERS_PAGE = "/admin/users";
export const ADMIN_CATEGORIES_PAGE = "/admin/categories";
export const ADMIN_LOCATIONS_PAGE = "/admin/locations";
export const ADMIN_PAYMENT_PAGE = "/admin/payment";
export const ADMIN_SUBCATEGORIES_PAGE = "/admin/categories/:categoryId";

+ 9
- 0
src/i18n/resources/rs.js View File

@@ -429,6 +429,15 @@ export default {
searchPlaceholder: "Pretražite korisnike....",
checkProfile: "Pogledaj profil",
},
navigation: {
role: "Administrator",
menu: "Meni",
users: "Korisnici",
categories: "Kategorije",
locations: "Lokacije",
payment: "Uplate",
logout: "Odjavi se",
},
categories: {
checkCategory: "Pogledaj podkategorije",
noOfOffers: "Broj objava: ",

+ 4
- 3
src/layouts/MainLayout/MainLayout.js View File

@@ -5,13 +5,13 @@ import { Grid } from "@mui/material";

const MainLayout = (props) => {
return (
<MainLayoutContainer>
<MainLayoutContainer className={props.className}>
{props.children}
<Grid container maxHeight="xl">
<LeftCard item xs={0} sm={0} md={3} lg={3} xl={2.4} >
<LeftCard item xs={0} sm={0} md={3} lg={3} xl={2.4}>
{props.leftCard}
</LeftCard>
<Content item xs={12} sm={12} md={9} lg={9} xl={9.6} >
<Content item xs={12} sm={12} md={9} lg={9} xl={9.6}>
{props.content}
</Content>
</Grid>
@@ -24,6 +24,7 @@ MainLayout.propTypes = {
leftCard: PropTypes.node,
content: PropTypes.node,
rightCard: PropTypes.node,
className: PropTypes.string,
};

export default MainLayout;

src/pages/AdminCategoriesPage/AdminCategoriesPage.js → src/pages/AdminHomePage/AdminCategoriesPage/AdminCategoriesPage.js View File


src/pages/AdminCategoriesPage/AdminCategoriesPage.styled.js → src/pages/AdminHomePage/AdminCategoriesPage/AdminCategoriesPage.styled.js View File


+ 66
- 9
src/pages/AdminHomePage/AdminHomePage.js View File

@@ -1,17 +1,74 @@
import React from "react";
import React, { useMemo } from "react";
import PropTypes from "prop-types";
import MarketPlace from "../../components/MarketPlace/MarketPlace";
import useOffers from "../../hooks/useOffers/useOffers";
// import MarketPlace from "../../components/MarketPlace/MarketPlace";
// import useOffers from "../../hooks/useOffers/useOffers";
import Sidebar from "../../components/Admin/Sidebar/Sidebar";
import { MainLayoutAdminHomePage } from "./AdminHomePage.styled";
import { useSelector } from "react-redux";
import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors";
import { OFFERS_SCOPE } from "../../store/actions/offers/offersActionConstants";
// import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors";
// import { OFFERS_SCOPE } from "../../store/actions/offers/offersActionConstants";
import AdminUsersPage from "../AdminUsersPage/AdminUsersPage";
import { selectMineProfile } from "../../store/selectors/profileSelectors";
import { Switch, useHistory } from "react-router-dom";
import {
ADMIN_CATEGORIES_PAGE,
ADMIN_LOCATIONS_PAGE,
ADMIN_PAYMENT_PAGE,
ADMIN_SUBCATEGORIES_PAGE,
ADMIN_USERS_PAGE,
HOME_PAGE,
} from "../../constants/pages";
import { selectUserId } from "../../store/selectors/loginSelectors";
import AdminCategoriesPage from "../AdminCategoriesPage/AdminCategoriesPage";
import AdminRoute from "../../components/Router/AdminRoute";
import AdminSubcategoriesPage from "./AdminSubcategoriesPage/AdminSubcategoriesPage";
import AdminLocationsPage from "./AdminLocationsPage/AdminLocationsPage";
import AdminPaymentPage from "./AdminPaymentPage/AdminPaymentPage";

const AdminHomePage = () => {
const offers = useOffers();
const isLoadingOffers = useSelector(
selectIsLoadingByActionType(OFFERS_SCOPE)
const profile = useSelector(selectMineProfile);
const userId = useSelector(selectUserId);
const history = useHistory();
const isUserLogin = useMemo(() => {
if (userId?.length === 0) return false;
return true;
}, [userId]);
if (!profile.roles.includes("Admin") || !isUserLogin) {
history.push(HOME_PAGE);
}
return (
<MainLayoutAdminHomePage
leftCard={<Sidebar />}
content={
<Switch>
<AdminRoute path={ADMIN_USERS_PAGE} component={AdminUsersPage} />
<AdminRoute
exact
path={ADMIN_CATEGORIES_PAGE}
component={AdminCategoriesPage}
/>
<AdminRoute
path={ADMIN_SUBCATEGORIES_PAGE}
component={AdminSubcategoriesPage}
/>
<AdminRoute
path={ADMIN_SUBCATEGORIES_PAGE}
component={AdminSubcategoriesPage}
/>
<AdminRoute
path={ADMIN_LOCATIONS_PAGE}
component={AdminLocationsPage}
/>
<AdminRoute path={ADMIN_PAYMENT_PAGE} component={AdminPaymentPage} />
<AdminRoute
path={ADMIN_SUBCATEGORIES_PAGE}
component={AdminSubcategoriesPage}
/>
<AdminRoute component={AdminUsersPage} />
</Switch>
}
/>
);
return <MarketPlace offers={offers} isAdmin skeleton={isLoadingOffers} />;
};

AdminHomePage.propTypes = {

+ 6
- 0
src/pages/AdminHomePage/AdminHomePage.styled.js View File

@@ -0,0 +1,6 @@
import styled from "styled-components";
import MainLayout from "../../layouts/MainLayout/MainLayout";

export const MainLayoutAdminHomePage = styled(MainLayout)`
margin-top: 0;
`;

+ 15
- 0
src/pages/AdminHomePage/AdminLocationsPage/AdminLocationsPage.js View File

@@ -0,0 +1,15 @@
import React from "react";
import PropTypes from "prop-types";
import { AdminLocationsPageContainer } from "./AdminLocationsPage.styled";

const AdminLocationsPage = () => {
return (
<AdminLocationsPageContainer>Admin locations</AdminLocationsPageContainer>
);
};

AdminLocationsPage.propTypes = {
children: PropTypes.node,
};

export default AdminLocationsPage;

+ 6
- 0
src/pages/AdminHomePage/AdminLocationsPage/AdminLocationsPage.styled.js View File

@@ -0,0 +1,6 @@
import { Box } from "@mui/material";
import styled from "styled-components";

export const AdminLocationsPageContainer = styled(Box)`
`

+ 15
- 0
src/pages/AdminHomePage/AdminPaymentPage/AdminPaymentPage.js View File

@@ -0,0 +1,15 @@
import React from 'react'
import PropTypes from 'prop-types'
import { AdminPaymentPageContainer } from './AdminPaymentPage.styled'

const AdminPaymentPage = () => {
return (
<AdminPaymentPageContainer>Admin payment</AdminPaymentPageContainer>
)
}

AdminPaymentPage.propTypes = {
children: PropTypes.node,
}

export default AdminPaymentPage

+ 6
- 0
src/pages/AdminHomePage/AdminPaymentPage/AdminPaymentPage.styled.js View File

@@ -0,0 +1,6 @@
import { Box } from "@mui/material";
import styled from "styled-components";

export const AdminPaymentPageContainer = styled(Box)`
`

src/pages/AdminSubcategoriesPage/AdminSubcategoriesPage.js → src/pages/AdminHomePage/AdminSubcategoriesPage/AdminSubcategoriesPage.js View File

@@ -2,8 +2,8 @@ import React from "react";
import PropTypes from "prop-types";
import { useDispatch, useSelector } from "react-redux";
import { useEffect } from "react";
import { fetchCategories } from "../../store/actions/categories/categoriesActions";
import { selectCategories } from "../../store/selectors/categoriesSelectors";
import { fetchCategories } from "../../../store/actions/categories/categoriesActions";
import { selectCategories } from "../../../store/selectors/categoriesSelectors";
import { useTranslation } from "react-i18next";
import {
AdminSubcategoriesHeader,
@@ -12,14 +12,16 @@ import {
NewSubcategoryButton,
SponsoredCategoryCard,
} from "./AdminSubcategoriesPage.styled";
import { selectManualSearchString } from "../../store/selectors/filtersSelectors";
import { selectManualSearchString } from "../../../store/selectors/filtersSelectors";
import { useMemo } from "react";
import { setManualSearchString } from "../../store/actions/filters/filtersActions";
import { setManualSearchString } from "../../../store/actions/filters/filtersActions";
import { useRouteMatch } from "react-router-dom";
import CategoryCard from "../../components/Cards/CategoryCard/CategoryCard";
import selectedTheme from "../../themes";
import CategoryCard from "../../../components/Cards/CategoryCard/CategoryCard";
import selectedTheme from "../../../themes";
import { useState } from "react";
import EditCategory from "../../components/Modals/EditCategory/EditCategory";
import EditCategory from "../../../components/Modals/EditCategory/EditCategory";
import { isInRoute } from "../../../util/helpers/routeHelpers";
import { ADMIN_SUBCATEGORIES_PAGE } from "../../../constants/pages";

const AdminSubcategoriesPage = () => {
const { t } = useTranslation();
@@ -29,6 +31,8 @@ const AdminSubcategoriesPage = () => {
const manualSearchString = useSelector(selectManualSearchString);
const [openedAddModal, setOpenedAddModal] = useState(false);

console.log(isInRoute(ADMIN_SUBCATEGORIES_PAGE));

useEffect(() => {
dispatch(fetchCategories());
}, []);

src/pages/AdminSubcategoriesPage/AdminSubcategoriesPage.styled.js → src/pages/AdminHomePage/AdminSubcategoriesPage/AdminSubcategoriesPage.styled.js View File

@@ -1,10 +1,10 @@
import { Box } from "@mui/material";
import styled from "styled-components";
import { PrimaryButton } from "../../components/Buttons/PrimaryButton/PrimaryButton";
import CategoryCard from "../../components/Cards/CategoryCard/CategoryCard";
import Header from "../../components/MarketPlace/Header/Header";
import SearchField from "../../components/TextFields/SearchField/SearchField";
import selectedTheme from "../../themes";
import { PrimaryButton } from "../../../components/Buttons/PrimaryButton/PrimaryButton";
import CategoryCard from "../../../components/Cards/CategoryCard/CategoryCard";
import Header from "../../../components/MarketPlace/Header/Header";
import SearchField from "../../../components/TextFields/SearchField/SearchField";
import selectedTheme from "../../../themes";

export const AdminSubcategoriesPageContainer = styled(Box)`
padding: 60px;

src/pages/AdminUsersPage/AdminUsersPage.js → src/pages/AdminHomePage/AdminUsersPage/AdminUsersPage.js View File


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

@@ -5,9 +5,9 @@ const request = axios.create({
// baseURL: "http://192.168.88.150:3001/", // DJOLE
// baseURL: "http://192.168.88.175:3005/",
// baseURL: "http://192.168.88.143:3001/", // DULE
// baseURL: "https://trampa-api-test.dilig.net/",
baseURL: "https://trampa-api-test.dilig.net/",
// baseURL: "http://localhost:3001/",
baseURL: process.env.REACT_APP_BASE_API_URL,
// baseURL: process.env.REACT_APP_BASE_API_URL,
headers: {
"Content-Type": "application/json",
},

+ 2
- 1
src/request/loginRequest.js View File

@@ -4,7 +4,8 @@ import apiEndpoints from "./apiEndpoints";
export const attemptLogin = (payload) =>
postRequest(apiEndpoints.authentications.login, payload);


export const logoutUserRequest = (payload) =>
postRequest(apiEndpoints.authentications.logout, payload);

export const logoutAdminRequest = (payload) =>
postRequest(apiEndpoints.authentications.logout, payload);

+ 10
- 11
src/store/actions/login/loginActionConstants.js View File

@@ -6,26 +6,25 @@ import {
createSuccessType,
createSubmitType,
createUpdateType,
} from '../actionHelpers';
} from "../actionHelpers";


const LOGIN_USER_SCOPE = 'LOGIN_USER';
const LOGIN_USER_SCOPE = "LOGIN_USER";
export const LOGIN_USER_FETCH = createFetchType(LOGIN_USER_SCOPE);
export const LOGIN_USER_SUCCESS = createSuccessType(LOGIN_USER_SCOPE);
export const LOGIN_USER_ERROR = createErrorType(LOGIN_USER_SCOPE);
export const CLEAR_LOGIN_USER_ERROR = createClearType(
`${LOGIN_USER_SCOPE}_ERROR`,
`${LOGIN_USER_SCOPE}_ERROR`
);
export const LOGIN_USER_LOADING = createLoadingType(LOGIN_USER_SCOPE);

export const UPDATE_USER_JWT_TOKEN = createUpdateType("UPDATE_USER_JWT_TOKEN");
export const RESET_LOGIN_STATE = createClearType("UPDATE_USER_JWT_TOKEN");
export const AUTHENTICATE_USER = createUpdateType("AUTHENTICATE_USER");
export const LOGOUT_USER = createUpdateType("LOGOUT_USER");
export const LOGOUT_ADMIN = createUpdateType("LOGOUT_ADMIN");
export const REFRESH_TOKEN = createUpdateType("REFRESH_TOKEN");

export const UPDATE_USER_JWT_TOKEN = createUpdateType('UPDATE_USER_JWT_TOKEN');
export const RESET_LOGIN_STATE = createClearType('UPDATE_USER_JWT_TOKEN');
export const AUTHENTICATE_USER = createUpdateType('AUTHENTICATE_USER');
export const LOGOUT_USER = createUpdateType('LOGOUT_USER');
export const REFRESH_TOKEN = createUpdateType('REFRESH_TOKEN');

const GENERATE_TOKEN_SCOPE = 'GENERATE_TOKEN';
const GENERATE_TOKEN_SCOPE = "GENERATE_TOKEN";
export const GENERATE_TOKEN = createSubmitType(GENERATE_TOKEN_SCOPE);
export const GENERATE_TOKEN_SUCCESS = createSuccessType(GENERATE_TOKEN_SCOPE);
export const GENERATE_TOKEN_ERROR = createErrorType(GENERATE_TOKEN_SCOPE);

+ 8
- 3
src/store/actions/login/loginActions.js View File

@@ -11,8 +11,8 @@ import {
GENERATE_TOKEN,
GENERATE_TOKEN_SUCCESS,
GENERATE_TOKEN_ERROR,
} from './loginActionConstants';
LOGOUT_ADMIN,
} from "./loginActionConstants";

export const fetchLogin = (payload) => ({
type: LOGIN_USER_FETCH,
@@ -51,9 +51,14 @@ export const logoutUser = (payload) => ({
payload,
});

export const logoutAdmin = (payload) => ({
type: LOGOUT_ADMIN,
payload,
});

export const refreshUserToken = (payload) => ({
type: REFRESH_TOKEN,
payload
payload,
});

export const generateToken = (payload) => ({

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

@@ -15,10 +15,10 @@ import { logoutUser, refreshUserToken } from "../actions/login/loginActions";
//Change URL with .env
// const baseURL = "http://192.168.88.143:3001/"; // DULE
// const baseURL = "http://192.168.88.175:3005/";
// const baseURL = "https://trampa-api-test.dilig.net/";
const baseURL = "https://trampa-api-test.dilig.net/";
// const baseURL = "http://192.168.88.150:3001/"; // DJOLE
// const baseURL = "http://localhost:3001/";
const baseURL = process.env.REACT_APP_BASE_API_URL
// const baseURL = process.env.REACT_APP_BASE_API_URL

//Interceptor unique name
export const accessTokensMiddlewareInterceptorName = "ACCESS_TOKEN_INTERCEPTOR";

+ 51
- 13
src/store/saga/loginSaga.js View File

@@ -4,11 +4,13 @@ import history from "../utils/history";
import {
AUTHENTICATE_USER,
LOGIN_USER_FETCH,
LOGOUT_ADMIN,
LOGOUT_USER,
REFRESH_TOKEN,
} from "../actions/login/loginActionConstants";
import {
attemptLogin,
logoutAdminRequest,
logoutUserRequest,
} from "../../request/loginRequest";
import {
@@ -17,7 +19,7 @@ import {
resetLoginState,
setUserJwtToken,
} from "../actions/login/loginActions";
import { LOGIN_PAGE } from "../../constants/pages";
import { ADMIN_LOGIN_PAGE, LOGIN_PAGE } from "../../constants/pages";
import { addHeaderToken, removeHeaderToken } from "../../request";
import {
JWT_REFRESH_TOKEN,
@@ -33,7 +35,10 @@ import {
import { rejectErrorCodeHelper } from "../../util/helpers/rejectErrorCodeHelper";
import i18next from "i18next";
import { attemptFetchProfile } from "../../request/profileRequest";
import { clearProfile, setMineProfile } from "../actions/profile/profileActions";
import {
clearProfile,
setMineProfile,
} from "../actions/profile/profileActions";
import { clearOffers } from "../actions/offers/offersActions";
import { clearChat } from "../actions/chat/chatActions";

@@ -47,7 +52,7 @@ function* fetchLogin({ payload }) {
const refresh = data.refresh;
const tokenDecoded = jwt.decode(token);
const refreshDecoded = jwt.decode(refresh);
if(isAdmin && !tokenDecoded.roles.includes("Admin")){
if (isAdmin && !tokenDecoded.roles.includes("Admin")) {
throw Error("Not an admin login on /login");
}
const accessToken = {
@@ -65,13 +70,19 @@ function* fetchLogin({ payload }) {
yield call(addHeaderToken, token);
const profileData = yield call(attemptFetchProfile, userId);
if (profileData) yield put(setMineProfile(profileData.data));
yield put(fetchUserSuccess({jwtToken: accessToken, refreshToken: refreshToken, userId}));
yield put(
fetchUserSuccess({
jwtToken: accessToken,
refreshToken: refreshToken,
userId,
})
);
if (payload.handleApiResponseSuccess) {
yield call(payload.handleApiResponseSuccess);
}
}
} catch (e) {
if(e.message){
if (e.message) {
yield put(fetchUserError(e.message));
}
if (e.response && e.response.data) {
@@ -82,7 +93,7 @@ function* fetchLogin({ payload }) {
let errorMessage = yield call(rejectErrorCodeHelper, e.response.status);
if (e.response.status === 401 || e.response.status === 404) {
errorMessage = i18next.t("login.wrongCredentials", {
lng: "rs"
lng: "rs",
});
}
yield put(fetchUserError(errorMessage));
@@ -115,9 +126,9 @@ function* logout(payload) {
const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN);
const user = yield call(jwt.decode, JwtToken);
if (user) {
const requestBody = {token: JwtToken}
const requestBody = { token: JwtToken };
yield call(logoutUserRequest, requestBody);
yield call(payload.payload)
yield call(payload.payload);
}
} catch (error) {
console.dir(error); // eslint-disable-line
@@ -131,16 +142,42 @@ function* logout(payload) {
yield put(clearChat());
}
}
function* refreshUserToken({payload}) {
function* logoutAdmin(payload) {
try {
const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN);
const user = yield call(jwt.decode, JwtToken);
if (user) {
const requestBody = { token: JwtToken };
yield call(logoutAdminRequest, requestBody);
yield call(payload.payload);
}
} catch (error) {
console.dir(error); // eslint-disable-line
} finally {
yield call(authScopeClearHelper);
yield call(removeHeaderToken);
yield put(resetLoginState());
yield call(history.replace, ADMIN_LOGIN_PAGE);
yield put(clearProfile());
yield put(clearOffers());
yield put(clearChat());
}
}
function* refreshUserToken({ payload }) {
try {
const newTokenDecoded = jwt.decode(payload);

yield put(setUserJwtToken({token: payload, exp: newTokenDecoded.exp}));
yield put(
setUserJwtToken({
token: payload,
exp: newTokenDecoded.exp,
roles: newTokenDecoded.roles,
})
);
yield call(addHeaderToken, payload);
yield call(authScopeSetHelper, JWT_TOKEN, payload);
return true;
}
catch(e){
} catch (e) {
console.dir(e);
return false;
}
@@ -151,6 +188,7 @@ export default function* loginSaga() {
takeLatest(LOGIN_USER_FETCH, fetchLogin),
takeLatest(AUTHENTICATE_USER, authenticateUser),
takeLatest(LOGOUT_USER, logout),
takeLatest(REFRESH_TOKEN, refreshUserToken)
takeLatest(REFRESH_TOKEN, refreshUserToken),
takeLatest(LOGOUT_ADMIN, logoutAdmin),
]);
}

+ 4
- 0
src/store/selectors/loginSelectors.js View File

@@ -9,3 +9,7 @@ export const selectLoginError = createSelector(
loginSelector,
(state) => state.errorMessage
);
export const selectRoles = createSelector(
loginSelector,
(state) => state.jwtToken.roles
);

+ 26
- 7
src/util/helpers/routeHelpers.js View File

@@ -1,7 +1,9 @@
import {
ADMIN_CATEGORIES_PAGE,
ADMIN_HOME_PAGE,
ADMIN_LOCATIONS_PAGE,
ADMIN_LOGIN_PAGE,
ADMIN_PAYMENT_PAGE,
ADMIN_SUBCATEGORIES_PAGE,
ADMIN_USERS_PAGE,
FORGOT_PASSWORD_MAIL_SENT,
@@ -13,11 +15,12 @@ import {
} from "../../constants/pages";
import history from "../../store/utils/history";

export const routeMatches = (route) => {
export const routeMatches = (route, secondRoute = null) => {
let routeToCheck = secondRoute || history.location.pathname;
if (
history.location.pathname === route ||
history.location.pathname + "/" === route ||
history.location.pathname.slice(0, -1) === route
routeToCheck === route ||
routeToCheck + "/" === route ||
routeToCheck.slice(0, -1) === route
)
return true;
return false;
@@ -50,14 +53,30 @@ export const isAdminRoute = () => {
routeMatches(ADMIN_HOME_PAGE) ||
routeMatches(ADMIN_USERS_PAGE) ||
routeMatches(ADMIN_CATEGORIES_PAGE) ||
dynamicRouteMatches(ADMIN_SUBCATEGORIES_PAGE)
dynamicRouteMatches(ADMIN_SUBCATEGORIES_PAGE) ||
routeMatches(ADMIN_LOCATIONS_PAGE) ||
routeMatches(ADMIN_PAYMENT_PAGE)
)
return true;
return false;
};
export const dynamicRouteMatches = (dynamicRoute) => {
const charactersToDelete =
(dynamicRoute.length - dynamicRoute.indexOf(":")) * -1;
let indexOfDynamicChar = dynamicRoute.indexOf(":");
if (indexOfDynamicChar === -1) return false;
const charactersToDelete = (dynamicRoute.length - indexOfDynamicChar) * -1;
const newDynamicRoute = dynamicRoute.slice(0, charactersToDelete);
return history.location.pathname.includes(newDynamicRoute);
};

export const isInRoute = (routeToCheck) => {
console.log("routeToCheck", routeToCheck);
console.log(
"first Expression",
history.location.pathname.includes(routeToCheck)
);
console.log("second expression", dynamicRouteMatches(routeToCheck));
return (
history.location.pathname.includes(routeToCheck) ||
dynamicRouteMatches(routeToCheck)
);
};

Loading…
Cancel
Save