Kaynağa Gözat

Added registration

master
Lazar Kostic 3 yıl önce
ebeveyn
işleme
a26707fc93

+ 3
- 0
src/AppRoutes.js Dosyayı Görüntüle

@@ -9,6 +9,7 @@ import {
ERROR_PAGE,
BASE_PAGE,
GOOGLE_AUTH_CALLBACK_PAGE,
REGISTER_PAGE,
} from "./constants/pages";

import LoginPage from "./pages/LoginPage/LoginPageMUI";
@@ -18,11 +19,13 @@ import ErrorPage from "./pages/ErrorPages/ErrorPage";
import ForgotPasswordPage from "./pages/ForgotPasswordPage/ForgotPasswordPageMUI";
import PrivateRoute from "./components/Router/PrivateRoute";
import GoogleAuthCallback from "./pages/GoogleAuthCallback/GoogleAuthCallback";
import RegisterPage from "./pages/RegisterPage/RegisterPageMUI";

const AppRoutes = () => (
<Switch>
<Route exact path={BASE_PAGE} component={LoginPage} />
<Route exact path={LOGIN_PAGE} component={LoginPage} />
<Route exact path={REGISTER_PAGE} component={RegisterPage} />
<Route path={GOOGLE_AUTH_CALLBACK_PAGE} component={GoogleAuthCallback} />
<Route path={NOT_FOUND_PAGE} component={NotFoundPage} />
<Route path={ERROR_PAGE} component={ErrorPage} />

+ 1
- 0
src/constants/pages.js Dosyayı Görüntüle

@@ -1,5 +1,6 @@
export const BASE_PAGE = '/';
export const LOGIN_PAGE = '/login';
export const REGISTER_PAGE = '/register';
export const FORGOT_PASSWORD_PAGE = '/forgot-password';
export const HOME_PAGE = '/home';
export const ERROR_PAGE = '/error-page';

+ 8
- 0
src/i18n/resources/en.js Dosyayı Görüntüle

@@ -40,6 +40,14 @@ export default {
range: '{{start}} to {{end}}',
},
},
register: {
registerTitle: "Register",
usernameRequired: 'Username is required.',
emailFormat: 'Invalid email address format.',
emailRequired: 'An email or username is required.',
passwordLength: 'Your password contain between 8 and 50 characters.',
passwordRequired: 'A Password is required.',
},
login: {
welcome: 'React template',
dontHaveAccount: "Don't have an account? ",

+ 6
- 0
src/initialValues/registerInitialValues.js Dosyayı Görüntüle

@@ -0,0 +1,6 @@
export default {
username: "",
email: "",
password: "",
};

+ 2
- 2
src/pages/LoginPage/LoginPageMUI.js Dosyayı Görüntüle

@@ -10,7 +10,7 @@ import {
fetchUser,
} from "../../store/actions/login/loginActions";
import { selectLoginError } from "../../store/selectors/loginSelectors";
import { FORGOT_PASSWORD_PAGE, HOME_PAGE } from "../../constants/pages";
import { FORGOT_PASSWORD_PAGE, HOME_PAGE, REGISTER_PAGE } from "../../constants/pages";
import {
Box,
Button,
@@ -173,7 +173,7 @@ const LoginPage = ({ history }) => {
sx={{ textAlign: { xs: "center", md: "right" } }}
>
<Link
to="#"
to={REGISTER_PAGE}
component={NavLink}
variant="body2"
underline="hover"

+ 215
- 0
src/pages/RegisterPage/RegisterPageMUI.js Dosyayı Görüntüle

@@ -0,0 +1,215 @@
/* eslint-disable */
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import { useFormik } from "formik";
import { useDispatch, useSelector } from "react-redux";
import { NavLink } from "react-router-dom";
import { useTranslation } from "react-i18next";
import {
clearRegisterErrors,
registerUser,
} from "../../store/actions/register/registerActions";
import { selectRegisterError } from "../../store/selectors/registerSelectors";
import {
FORGOT_PASSWORD_PAGE,
HOME_PAGE,
LOGIN_PAGE,
} from "../../constants/pages";
import {
Box,
Button,
Container,
Grid,
IconButton,
InputAdornment,
Link,
TextField,
Typography,
} from "@mui/material";
import { Visibility, VisibilityOff } from "@mui/icons-material";
import Backdrop from "../../components/MUI/BackdropComponent";
import ErrorMessage from "../../components/MUI/ErrorMessageComponent";
import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors";
import { LOGIN_USER_SCOPE } from "../../store/actions/login/loginActionConstants";
import GoogleIcon from "@mui/icons-material/Google";
import registerInitialValues from "../../initialValues/registerInitialValues";
import registerValidation from "../../validations/registerValidation";
import { REGISTER_USER_SCOPE } from "../../store/actions/register/registerActionConstants";
import { makeToastMessage } from "../../util/helpers/toastMessage";

const RegisterPage = ({ history }) => {
const dispatch = useDispatch();
const { t } = useTranslation();
const error = useSelector(selectRegisterError);

const [showPassword, setShowPassword] = useState(false);
const handleClickShowPassword = () => setShowPassword(!showPassword);
const handleMouseDownPassword = () => setShowPassword(!showPassword);

const handleGoogle = () => {
window.location = "http://localhost:1337/api/connect/google";
};

// Clear login errors when user firstly enters the page
useEffect(() => {
dispatch(clearRegisterErrors());
}, []);

const isLoading = useSelector(
selectIsLoadingByActionType(REGISTER_USER_SCOPE)
);

const handleApiResponseSuccess = () => {
history.push({
pathname: LOGIN_PAGE,
state: {
from: history.location.pathname,
},
});
makeToastMessage("User successfuly registered. Please login.");
};

const handleSubmit = (values) => {
const { username, email, password } = values;
dispatch(clearRegisterErrors());
dispatch(
registerUser({ username, email, password, handleApiResponseSuccess })
);
};

const formik = useFormik({
initialValues: registerInitialValues,
validationSchema: registerValidation,
onSubmit: handleSubmit,
validateOnBlur: true,
enableReinitialize: true,
});

return (
<Container component="main" maxWidth="md">
<Box
sx={{
marginTop: 32,
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<Typography component="h1" variant="h5">
{t("register.registerTitle")}
</Typography>
{error && <ErrorMessage error={error} />}
<Box
component="form"
onSubmit={formik.handleSubmit}
sx={{ position: "relative", mt: 1, p: 1 }}
>
<Backdrop position="absolute" isLoading={isLoading} />
<TextField
name="username"
label={t("common.labelUsername")}
margin="normal"
value={formik.values.username}
onChange={formik.handleChange}
error={formik.touched.username && Boolean(formik.errors.username)}
helperText={formik.touched.username && formik.errors.username}
autoFocus
fullWidth
/>
<TextField
name="email"
label={t("common.labelEmail")}
margin="normal"
value={formik.values.email}
onChange={formik.handleChange}
error={formik.touched.email && Boolean(formik.errors.email)}
helperText={formik.touched.email && formik.errors.email}
fullWidth
/>
<TextField
name="password"
label={t("common.labelPassword")}
margin="normal"
type={showPassword ? "text" : "password"}
value={formik.values.password}
onChange={formik.handleChange}
error={formik.touched.password && Boolean(formik.errors.password)}
helperText={formik.touched.password && formik.errors.password}
fullWidth
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
>
{showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
),
}}
/>
<Button
type="submit"
variant="contained"
sx={{ mt: 3, mb: 2 }}
fullWidth
>
{t("register.registerTitle")}
</Button>
<Button
onClick={handleGoogle}
startIcon={<GoogleIcon />}
fullWidth
variant="outlined"
>
Connect with Google
</Button>
<Grid container>
<Grid
item
xs={12}
md={6}
sx={{ textAlign: { xs: "center", md: "left" } }}
>
<Link
to={FORGOT_PASSWORD_PAGE}
component={NavLink}
variant="body2"
underline="hover"
>
{t("login.forgotYourPassword")}
</Link>
</Grid>
<Grid
item
xs={12}
md={6}
sx={{ textAlign: { xs: "center", md: "right" } }}
>
<Link
to={LOGIN_PAGE}
component={NavLink}
variant="body2"
underline="hover"
>
{t("login.logIn")}
</Link>
</Grid>
</Grid>
</Box>
</Box>
</Container>
);
};

RegisterPage.propTypes = {
history: PropTypes.shape({
replace: PropTypes.func,
push: PropTypes.func,
location: PropTypes.shape({
pathname: PropTypes.string,
}),
}),
};
export default RegisterPage;

+ 1
- 1
src/request/index.js Dosyayı Görüntüle

@@ -6,7 +6,7 @@ const request = axios.create({
headers: {
"Content-Type": "application/json",
},
// withCredentials: true,
withCredentials: true,
// paramsSerializer: (params) =>
// queryString.stringify(params, { arrayFormat: 'comma' }),
});

+ 4
- 2
src/request/loginRequest.js Dosyayı Görüntüle

@@ -9,14 +9,16 @@ export const getUsernames = (emailorusername) =>
export const attemptLogin = (payload) =>
postRequest(apiEndpoints.authentications.login, payload);

export const attemptRegister = (payload) =>
postRequest(apiEndpoints.authentications.register, payload);

export const updateSecurityAnswer = (payload) =>
postRequest(apiEndpoints.authentications.confirmSecurityQuestion, payload);

export const refreshTokenRequest = (payload) =>
postRequest(apiEndpoints.authentications.refreshToken, payload);

export const logoutUserRequest = () =>
getRequest(apiEndpoints.users.logout);
export const logoutUserRequest = () => getRequest(apiEndpoints.users.logout);

export const generateTokenRequest = (payload) =>
postRequest(apiEndpoints.authentications.generateToken, payload);

+ 18
- 0
src/store/actions/register/registerActionConstants.js Dosyayı Görüntüle

@@ -0,0 +1,18 @@
import {
createClearType,
createErrorType,
createFetchType,
createLoadingType,
createSuccessType,
} from "../actionHelpers";

export const REGISTER_USER_SCOPE = "REGISTER_USER";
export const REGISTER_USER_FETCH = createFetchType(REGISTER_USER_SCOPE);
export const REGISTER_USER_SUCCESS = createSuccessType(REGISTER_USER_SCOPE);
export const REGISTER_USER_ERROR = createErrorType(REGISTER_USER_SCOPE);
export const CLEAR_REGISTER_USER_ERROR = createClearType(
`${REGISTER_USER_SCOPE}_ERROR`
);
export const REGISTER_USER_LOADING = createLoadingType(REGISTER_USER_SCOPE);

export const RESET_REGISTER_STATE = "RESET_REGISTER_STATE";

+ 30
- 0
src/store/actions/register/registerActions.js Dosyayı Görüntüle

@@ -0,0 +1,30 @@
import {
CLEAR_REGISTER_USER_ERROR,
REGISTER_USER_ERROR,
REGISTER_USER_FETCH,
REGISTER_USER_SUCCESS,
RESET_REGISTER_STATE,
} from "./registerActionConstants";

export const registerUser = (payload) => ({
type: REGISTER_USER_FETCH,
payload,
});

export const registerUserSuccess = (payload) => ({
type: REGISTER_USER_SUCCESS,
payload,
});

export const registerUserError = (payload) => ({
type: REGISTER_USER_ERROR,
payload,
});

export const resetRegisterState = () => ({
type: RESET_REGISTER_STATE,
});

export const clearRegisterErrors = () => ({
type: CLEAR_REGISTER_USER_ERROR,
});

+ 3
- 1
src/store/reducers/index.js Dosyayı Görüntüle

@@ -3,10 +3,12 @@ import loginReducer from './login/loginReducer';
import loadingReducer from './loading/loadingReducer';
import userReducer from './user/userReducer';
import randomDataReducer from './randomData/randomDataReducer';
import registerReducer from './register/registerReducer'

export default combineReducers({
login: loginReducer,
user: userReducer,
loading:loadingReducer,
randomData: randomDataReducer
randomData: randomDataReducer,
register: registerReducer,
});

+ 54
- 0
src/store/reducers/register/registerReducer.js Dosyayı Görüntüle

@@ -0,0 +1,54 @@
import createReducer from '../../utils/createReducer';
import {
CLEAR_REGISTER_USER_ERROR,
REGISTER_USER_ERROR,
REGISTER_USER_SUCCESS,
RESET_REGISTER_STATE,
} from '../../actions/register/registerActionConstants';

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

export default createReducer(
{

[REGISTER_USER_SUCCESS]: setUser,
[RESET_REGISTER_STATE]: resetRegisterState,
[REGISTER_USER_ERROR]: setError,
[CLEAR_REGISTER_USER_ERROR]: clearRegisterErrors,
},
initialState,
);


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

function setError(state, action) {
return {
...state,
errorMessage: action.payload,
};
}

function resetRegisterState() {
return initialState;
}

function clearRegisterErrors(state) {
return {
...state,
errorMessage: '',
};
}

+ 4
- 5
src/store/saga/index.js Dosyayı Görüntüle

@@ -1,8 +1,7 @@
import { all } from 'redux-saga/effects';
import loginSaga from './loginSaga';
import { all } from "redux-saga/effects";
import loginSaga from "./loginSaga";
import registerSaga from "./registerSaga";

export default function* rootSaga() {
yield all([
loginSaga(),
]);
yield all([loginSaga(), registerSaga()]);
}

+ 32
- 0
src/store/saga/registerSaga.js Dosyayı Görüntüle

@@ -0,0 +1,32 @@
import { all, call, put, takeLatest } from "@redux-saga/core/effects";
import { REGISTER_USER_FETCH } from "../actions/register/registerActionConstants";
import { attemptRegister } from "../../request/loginRequest";
import {
registerUserError,
registerUserSuccess,
} from "../actions/register/registerActions";
import { JWT_TOKEN } from "../../constants/localStorage";
import { authScopeSetHelper } from "../../util/helpers/authScopeHelpers";
import { rejectErrorCodeHelper } from "../../util/helpers/rejectErrorCodeHelper";

function* registerUser({ payload }) {
try {
const { data } = yield call(attemptRegister, payload);
if (data?.jwt) {
yield call(authScopeSetHelper, JWT_TOKEN, data.jwt);
}
yield put(registerUserSuccess(data.jwt));
if (payload.handleApiResponseSuccess) {
yield call(payload.handleApiResponseSuccess);
}
} catch (e) {
if (e.response && e.response.data) {
const errorMessage = yield call(rejectErrorCodeHelper, e);
yield put(registerUserError(errorMessage));
}
}
}

export default function* registerSaga() {
yield all([takeLatest(REGISTER_USER_FETCH, registerUser)]);
}

+ 8
- 0
src/store/selectors/registerSelectors.js Dosyayı Görüntüle

@@ -0,0 +1,8 @@
import { createSelector } from 'reselect';

const registerSelector = (state) => state.register;

export const selectRegisterError = createSelector(
registerSelector,
(state) => state.errorMessage,
);

+ 12
- 0
src/validations/registerValidation.js Dosyayı Görüntüle

@@ -0,0 +1,12 @@
import * as Yup from "yup";
import i18next from "i18next";

export default Yup.object().shape({
username: Yup.string().required("register.usernameRequired"),
email: Yup.string()
.email(i18next.t("register.emailFormat"))
.required(i18next.t("register.emailRequired")),
password: Yup.string()
.required(i18next.t("register.passwordRequired"))
.min(8, i18next.t("register.passwordLength")),
});

Loading…
İptal
Kaydet