Bladeren bron

Merge remote-tracking branch 'origin/FE_dev' into feature/google_login

feature/google_login
Ermin Bronja 3 jaren geleden
bovenliggende
commit
89e8e9ab38

+ 12
- 2
src/App.js Bestand weergeven

@@ -1,12 +1,22 @@
import React from "react";
import React, { useEffect } from "react";
import { Router } from "react-router-dom";
import { Helmet } from "react-helmet-async";
import i18next from "i18next";
import history from "./store/utils/history";
import MainContainer from "./components/Section/MainContainer";
import AppRoutes from "./AppRoutes";
import { useDispatch } from "react-redux";
import {refreshUserToken} from "./store/actions/login/loginActions";
import { BASE_PAGE } from "./constants/pages";
const App = () => {
// const { pathname } = useLocation();
const dispatch = useDispatch()

useEffect(() => {
if(history.location.pathname === BASE_PAGE)
return;
dispatch(refreshUserToken())
},[])

return (
<>
<Router history={history}>

+ 2
- 2
src/assets/styles/components/_nav.scss Bestand weergeven

@@ -1,6 +1,6 @@
.full-rounded{
height: 40px;
width: 40px;
height: 40px !important;
width: 40px !important;
font-size: 16px;
@include flex-center;
border-radius: 50%;

+ 31
- 0
src/assets/styles/components/_user-profile.scss Bestand weergeven

@@ -0,0 +1,31 @@
.user-view {
position: absolute;
width: 342px;
height: 317px;
background-color: red;
top: calc($navHeight + 1rem);
z-index: 1000;
right: -360px;
background: #ffffff;
/* Main Shadow 12% */
transition: .35s;
opacity: .25;

box-shadow: 4px 4px 18px rgba(29, 29, 29, 0.12);
border-radius: 18px;
animation: userView .35s !important;
}

.user-view.active{
right: 1rem;
opacity: 1;
}

@keyframes userView {
0%{
opacity: 0;
}
100%{
opacity: 1;
}
}

+ 48
- 6
src/components/MUI/NavbarComponent.js Bestand weergeven

@@ -1,4 +1,4 @@
import React, { useState, useMemo } from "react";
import React, { useState, useMemo, useRef, useEffect } from "react";
import {
AppBar,
// Badge,
@@ -29,6 +29,9 @@ import Drawer from "./DrawerComponent";
import { useTranslation } from "react-i18next";
import CloseIcon from "@mui/icons-material/Close";
import LogoutIcon from "@mui/icons-material/Logout";
import UserProfile from "../Profile/UserProfile";
import { useSelector } from "react-redux";
import { userSelector } from "../../store/selectors/userSelectors";

const NavbarComponent = () => {
const navItems = [
@@ -39,18 +42,46 @@ const NavbarComponent = () => {
"patterns",
"stats",
"users",
// "signOut"
];
const [openDrawer, setOpenDrawer] = useState(false);
const [preview, setPreview] = useState(false);
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down("sm"));
// const toggleColorMode = useContext(ColorModeContext);

let userRef = useRef();
let btnRef = useRef();

// get authenticated user
const user = useSelector(userSelector)

const { t } = useTranslation();

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

const userPreviewHandler = () => {
setPreview(!preview);
};

useEffect(() => {
let handler = (e) => {
if (userRef.current) {
if (!userRef.current.contains(e.target) && !btnRef.current.contains(e.target)) {
setPreview(false);
}
}
};

document.addEventListener("mousedown", handler, { capture: true });

return () => {
document.removeEventListener("mousedown", handler, { capture: true });
};
}, []);

const drawerContent = useMemo(
() => (
<div style={{ width: "100vw", padding: "36px" }}>
@@ -72,10 +103,10 @@ const NavbarComponent = () => {
<img src={avatarLogo} width="64px" height="64px" />
<Typography
variant="h5"
sx={{ fontSize: "18px", marginTop: "15px", }}
sx={{ fontSize: "18px", marginTop: "15px" }}
className="text-blue"
>
Danijela Ranđelović
{user.firstName + " " + user.lastName}
</Typography>
<Typography
variant="body1"
@@ -130,7 +161,7 @@ const NavbarComponent = () => {
borderRadius: "9px",
background: "white",
width: "90%",
marginTop: '50px'
marginTop: "50px",
}}
startIcon={<LogoutIcon />}
>
@@ -181,7 +212,10 @@ const NavbarComponent = () => {
>
{matches ? (
<Box>
<IconButton sx={{marginLeft: '15px'}} onClick={handleToggleDrawer}>
<IconButton
sx={{ marginLeft: "15px" }}
onClick={handleToggleDrawer}
>
<MenuOutlinedIcon />
</IconButton>
</Box>
@@ -239,9 +273,17 @@ const NavbarComponent = () => {
<div className="search-Icon">
<img src={searchIcon} />
</div>
<div className="full-rounded">DR</div>
<div
onClick={userPreviewHandler}
className="full-rounded user-avatar"
ref={btnRef}
>
{/* user.profilePic ? user.profilePic : */}
{user.firstName[0] + "" + user.lastName[0]}
</div>
</Box>
)}
{!matches && <UserProfile innerRef={userRef} show={preview}/>}
{/* <Box>
<MenuList />
</Box> */}

+ 61
- 0
src/components/Profile/UserProfile.js Bestand weergeven

@@ -0,0 +1,61 @@
import { Button, Typography } from "@mui/material";
import { Box } from "@mui/system";
import LogoutIcon from "@mui/icons-material/Logout";
import React from "react";
import { useTranslation } from "react-i18next";
import avatarLogo from "../../assets/images/Avatar.png";
import PropTypes from "prop-types";
import { userSelector } from "../../store/selectors/userSelectors";
import { useSelector } from "react-redux";

const UserProfile = ({ show, innerRef }) => {
const { t } = useTranslation();
// const theme = useTheme();
// const matches = useMediaQuery(theme.breakpoints.down("sm"));
const user = useSelector(userSelector);
return (
<Box className={`user-view flex-center ${show && "active"}`}>
<div
ref={innerRef}
className="flex-center flex-col"
style={{ width: "90%" }}
>
<img src={avatarLogo} width="64px" height="64px" />
<Typography
variant="h5"
sx={{ fontSize: "18px", marginTop: "15px" }}
className="text-blue"
>
{user.firstName + " " + user.lastName}
</Typography>
<Typography
variant="body1"
sx={{ fontSize: "14px", marginTop: "8px" }}
className="text-grey9d"
>
HR Specialist
</Typography>
{/* <div className="hr" style={{ width: "90%", marginTop: "18px" }}></div> */}

<Button
sx={{
padding: "18px 72px",
border: "1px solid #226cb0",
borderRadius: "9px",
background: "white",
width: "90%",
marginTop: "50px",
}}
startIcon={<LogoutIcon />}
>
{t("nav.signOut")}
</Button>
</div>
</Box>
);
};
UserProfile.propTypes = {
show: PropTypes.any,
innerRef: PropTypes.any,
};
export default UserProfile;

+ 2
- 1
src/i18n/resources/en.js Bestand weergeven

@@ -99,6 +99,7 @@ export default {
planer: 'Planer',
patterns: 'Patterns',
stats: 'Stats',
users: 'Users'
users: 'Users',
signOut: 'Sign Out'
}
};

+ 109
- 109
src/i18n/resources/rs.js Bestand weergeven

@@ -1,110 +1,110 @@
export default {
app: {
title: 'HR Centar'
},
refresh: {
// title: 'Are you active?',
// cta:
// "You were registered as not active, please confirm that you are active in the next minute, if you don't you will be logged out.",
},
common: {
// close: 'Close',
// trademark: 'TM',
// search: 'Search',
// error: 'Error',
// continue: 'Continue',
labelUsername: 'Korisničko ime',
labelPassword: 'Šifra',
or: 'ili',
// next: 'Next',
// nextPage: 'Next page',
// previousPage: 'Previous page',
// back: 'Back',
// goBack: 'Go Back',
// ok: 'Ok',
// done: 'Done',
// confirm: 'Confirm',
// printDownload: 'Print/Download',
// cancel: 'Cancel',
// remove: 'Remove',
// invite: 'Invite',
// save: 'Save',
// complete: 'Complete',
// download: 'Download',
// yes: 'Yes',
// no: 'No',
// to: 'to',
// select: 'Select...',
// none: 'None',
// date: {
// range: '{{start}} to {{end}}',
// },
},
login: {
welcome: 'Dobrodošli!',
// dontHaveAccount: "Don't have an account? ",
// emailFormat: 'Invalid email address format.',
// emailRequired: 'An email or username is required.',
// noUsers: 'There are no users with that email.',
// passwordStrength: 'Your password is {{strength}}.',
// passwordLength: 'Your password contain between 8 and 50 characters.',
// signUpRecommendation: 'Sign up',
// email: 'Please enter your email address or username to log in:',
logInTitle: 'Prijavi se',
logIn: 'Prijavi se',
// signUp: 'Sign Up',
usernameRequired: 'Potrebno je uneti korisničko ime.',
passwordRequired: 'Potrebno je uneti šifru.',
forgotYourPassword: 'Zaboravio/la si šifru?',
// _useDifferentEmail: 'Use different email address or username',
// get useDifferentEmail() {
// return this._useDifferentEmail;
// },
// set useDifferentEmail(value) {
// this._useDifferentEmail = value;
// },
signInWithGoogle: 'Prijava putem Google-a'
},
// password: {
// weak: 'weak',
// average: 'average',
// good: 'good',
// strong: 'strong',
// },
// forgotPassword: {
// title: 'Forgot Password',
// label: 'Send email',
// emailRequired: 'An email is required.',
// emailFormat: 'Invalid email address format.',
// forgotPassword: {
// title: 'Forgot Password',
// subtitle:
// 'Please answer the security question to gain access to your account:',
// label: 'Reset Password',
// },
// },
// notFound: {
// text: "We're sorry but we couldn't find the page you were looking for.",
// goBack: 'Go back to homepage',
// },
// errorPage: {
// text:
// "We're sorry, an internal server error came up. Please be patient or try again later.",
// goBack: 'Go back to homepage',
// logout: 'Logout',
// },
// apiErrors:{
// ClientIpAddressIsNullOrEmpty:"Client Ip address is null or empty",
// UsernameDoesNotExist: "Username does not exist"
// },
nav:{
ads: 'Oglasi',
selectionFlow: 'Tok Selekcije',
candidates: 'Kandidati',
planer: 'Planer',
patterns: 'Šabloni',
stats: 'Statistika',
users: 'Korisnici'
}
};
app: {
title: 'HR Centar'
},
refresh: {
// title: 'Are you active?',
// cta:
// "You were registered as not active, please confirm that you are active in the next minute, if you don't you will be logged out.",
},
common: {
// close: 'Close',
// trademark: 'TM',
// search: 'Search',
// error: 'Error',
// continue: 'Continue',
labelUsername: 'Korisničko ime',
labelPassword: 'Šifra',
or: 'ili',
// next: 'Next',
// nextPage: 'Next page',
// previousPage: 'Previous page',
// back: 'Back',
// goBack: 'Go Back',
// ok: 'Ok',
// done: 'Done',
// confirm: 'Confirm',
// printDownload: 'Print/Download',
// cancel: 'Cancel',
// remove: 'Remove',
// invite: 'Invite',
// save: 'Save',
// complete: 'Complete',
// download: 'Download',
// yes: 'Yes',
// no: 'No',
// to: 'to',
// select: 'Select...',
// none: 'None',
// date: {
// range: '{{start}} to {{end}}',
// },
},
login: {
welcome: 'Dobrodošli!',
// dontHaveAccount: "Don't have an account? ",
// emailFormat: 'Invalid email address format.',
// emailRequired: 'An email or username is required.',
// noUsers: 'There are no users with that email.',
// passwordStrength: 'Your password is {{strength}}.',
// passwordLength: 'Your password contain between 8 and 50 characters.',
// signUpRecommendation: 'Sign up',
// email: 'Please enter your email address or username to log in:',
logInTitle: 'Prijavi se',
logIn: 'Prijavi se',
// signUp: 'Sign Up',
usernameRequired: 'Potrebno je uneti korisničko ime.',
passwordRequired: 'Potrebno je uneti šifru.',
forgotYourPassword: 'Zaboravio/la si šifru?',
// _useDifferentEmail: 'Use different email address or username',
// get useDifferentEmail() {
// return this._useDifferentEmail;
// },
// set useDifferentEmail(value) {
// this._useDifferentEmail = value;
// },
signInWithGoogle: 'Prijava putem Google-a'
},
// password: {
// weak: 'weak',
// average: 'average',
// good: 'good',
// strong: 'strong',
// },
// forgotPassword: {
// title: 'Forgot Password',
// label: 'Send email',
// emailRequired: 'An email is required.',
// emailFormat: 'Invalid email address format.',
// forgotPassword: {
// title: 'Forgot Password',
// subtitle:
// 'Please answer the security question to gain access to your account:',
// label: 'Reset Password',
// },
// },
// notFound: {
// text: "We're sorry but we couldn't find the page you were looking for.",
// goBack: 'Go back to homepage',
// },
// errorPage: {
// text:
// "We're sorry, an internal server error came up. Please be patient or try again later.",
// goBack: 'Go back to homepage',
// logout: 'Logout',
// },
// apiErrors:{
// ClientIpAddressIsNullOrEmpty:"Client Ip address is null or empty",
// UsernameDoesNotExist: "Username does not exist"
// },
nav:{
ads: 'Oglasi',
selectionFlow: 'Tok Selekcije',
candidates: 'Kandidati',
planer: 'Planer',
patterns: 'Šabloni',
stats: 'Statistika',
users: 'Korisnici',
signOut: 'Izloguj se'
}
};

+ 1
- 0
src/main.scss Bestand weergeven

@@ -11,6 +11,7 @@
@import './assets/styles/components/radio';
@import './assets/styles/components/modal';
@import './assets/styles/components/auth-card';
@import './assets/styles/components/user-profile';
@import './assets/styles/components/auth';
@import './assets/styles/components/login';
@import './assets/styles/components/login-card';

+ 1
- 0
src/pages/ErrorPages/NotFoundPage.js Bestand weergeven

@@ -7,6 +7,7 @@ const NotFoundPage = () => {
const { t } = useTranslation();

return (
// <div className="c-error-page"> to be FIXED LATER!!!!
<div className="c-error-page">
<Section className="c-error-page__content-container">
<div className="c-error-page__content">

+ 10
- 11
src/pages/LoginPage/LoginPageMUI.js Bestand weergeven

@@ -43,17 +43,16 @@ const LoginPage = ({ history }) => {
const handleClickShowPassword = () => setShowPassword(!showPassword);
const handleMouseDownPassword = () => setShowPassword(!showPassword);

// When user refreshes page
// useEffect(() => {
// function redirectClient() {
// let token = localStorage.getItem("JwtToken")
// if (!token) {
// return;
// }
// handleApiResponseSuccess()
// }
// redirectClient();
// }, [history]);
useEffect(() => {
function redirectClient() {
let token = localStorage.getItem("JwtToken")
if (!token) {
return;
}
handleApiResponseSuccess()
}
redirectClient();
}, [history]);

const handleCallbackResponse = (response) => {
console.log(response.credential);

+ 1
- 0
src/request/apiEndpoints.js Bestand weergeven

@@ -4,5 +4,6 @@ export default {
authentications: {
login: base + '/users/authenticate',
googleLogin: base + '/users/authenticateGoogle',
refreshToken:'http://localhost:26081/v1/users/refresh'
},
};

+ 3
- 2
src/store/actions/login/loginActions.js Bestand weergeven

@@ -49,7 +49,7 @@ export const fetchGoogleUserError = (payload) => ({

export const updateUserToken = (payload) => ({
type: UPDATE_USER_JWT_TOKEN,
payload,
payload
});

export const resetLoginState = () => ({
@@ -68,8 +68,9 @@ export const logoutUser = () => ({
type: LOGOUT_USER,
});

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

export const generateToken = (payload) => ({

+ 2
- 29
src/store/reducers/login/loginReducer.js Bestand weergeven

@@ -2,27 +2,18 @@ import createReducer from '../../utils/createReducer';
import {
CLEAR_LOGIN_USER_ERROR,
LOGIN_USER_ERROR,
LOGIN_USER_SUCCESS,
RESET_LOGIN_STATE,
UPDATE_USER_JWT_TOKEN,
GENERATE_TOKEN_SUCCESS,
GENERATE_TOKEN_ERROR,
} from '../../actions/login/loginActionConstants';

const initialState = {
email: '',
token: {
RefreshToken: '',
JwtToken: '',
},
errorMessage: '',
email: "",
errorMessage: "",
};

export default createReducer(
{

[LOGIN_USER_SUCCESS]: setUser,
[UPDATE_USER_JWT_TOKEN]: setUserJwtToken,
[RESET_LOGIN_STATE]: resetLoginState,
[LOGIN_USER_ERROR]: setError,
[CLEAR_LOGIN_USER_ERROR]: clearLoginErrors,
@@ -32,24 +23,6 @@ export default createReducer(
initialState,
);


function setUser(state, action) {
return {
...state,
token: action.payload,
};
}

function setUserJwtToken(state, action) {
return {
...state,
token: {
...state.token,
JwtToken: action.payload,
},
};
}

function setError(state, action) {
return {
...state,

+ 12
- 3
src/store/reducers/user/userReducer.js Bestand weergeven

@@ -5,7 +5,12 @@ import {
} from '../../actions/user/userActionConstants';

const initialState = {
user: {},
id:"",
firstName:"",
lastName:"",
username:"",
token:"",
refreshToken:""
};

export default createReducer(
@@ -18,8 +23,12 @@ export default createReducer(

function setUser(state, action) {
return {
...state,
user: action.payload,
id:action.payload.id,
firstName:action.payload.firstName,
lastName:action.payload.lastName,
username:action.payload.username,
token:action.payload.token,
refreshToken:action.payload.refreshToken,
};
}


+ 16
- 22
src/store/saga/loginSaga.js Bestand weergeven

@@ -22,7 +22,7 @@ import {
resetLoginState,
updateUserToken,
} from '../actions/login/loginActions';
import { LOGIN_PAGE } from '../../constants/pages';
import { LOGIN_PAGE,BASE_PAGE } from '../../constants/pages';
import { setUser } from '../actions/user/userActions';
import {
addHeaderToken,
@@ -48,13 +48,12 @@ import { rejectErrorCodeHelper } from '../../util/helpers/rejectErrorCodeHelper'
function* fetchUser({ payload }) {
try {
const { data } = yield call(attemptLogin, payload);
if (data.token) {
const user = jwt.decode(data.token);
if (data) {
//const user = jwt.decode(data.token);
yield call(authScopeSetHelper, JWT_TOKEN, data.token);
yield call(authScopeSetHelper, JWT_REFRESH_TOKEN, data.JwtRefreshToken);
yield call(authScopeSetHelper, REFRESH_TOKEN_CONST, data.RefreshToken);
yield call(authScopeSetHelper, REFRESH_TOKEN_CONST, data.refreshToken);
yield call(addHeaderToken, data.token);
yield put(setUser(user));
yield put(setUser(data));
}
yield put(fetchUserSuccess(data));
if (payload.handleApiResponseSuccess) {
@@ -136,28 +135,23 @@ function* logoutUser() {

export function* refreshToken() {
try {
const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN);
const JwtRefreshToken = yield call(
authScopeStringGetHelper,
JWT_REFRESH_TOKEN,
);
const token = yield call(authScopeStringGetHelper, JWT_TOKEN);
const refreshToken = yield call(authScopeStringGetHelper,REFRESH_TOKEN_CONST);

if (JwtToken && JwtRefreshToken) {
if (token && refreshToken) {
const { data } = yield call(refreshTokenRequest, {
JwtRefreshToken,
JwtToken,
refreshToken,
token,
});

yield call(authScopeSetHelper, JWT_TOKEN, data.JwtToken);
yield call(authScopeSetHelper, JWT_REFRESH_TOKEN, data.JwtRefreshToken);
const user = jwt.decode(data.JwtToken);
addHeaderToken(data.JwtToken);
yield put(setUser(user));
yield put(updateUserToken(data.JwtToken));
yield call(authScopeSetHelper, JWT_TOKEN, data.data.token);
addHeaderToken(data.data.token);
yield put(setUser(data.data));
}
} catch (error) {
yield call(logoutUser);
console.log(error); // eslint-disable-line
localStorage.removeItem(JWT_TOKEN)
localStorage.removeItem(REFRESH_TOKEN_CONST)
yield call(history.replace, BASE_PAGE);
}
}


+ 0
- 4
src/util/helpers/authScopeHelpers.js Bestand weergeven

@@ -9,10 +9,6 @@ export function authScopeGetHelper(key) {
}

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

return localStorage.getItem(key);
}


Laden…
Annuleren
Opslaan