register form

This commit is contained in:
2019-06-26 17:28:38 +02:00
parent 6e427e6713
commit 9f7a319913
10 changed files with 327 additions and 96 deletions
+3 -2
View File
@@ -23,8 +23,9 @@ export const fetchCurrentUserFailure = user => ({
user user
}); });
export const registerUserRequest = () => ({ export const registerUserRequest = (payload) => ({
type: REQUEST_REGISTER type: REQUEST_REGISTER,
payload
}); });
export const registerUserSuccess = () => ({ export const registerUserSuccess = () => ({
+131 -46
View File
@@ -1,8 +1,15 @@
import React from 'react'; import React, {Fragment} from 'react';
import { TextField, FormGroup, FormControlLabel, Checkbox, Button, Typography, Link } from '@material-ui/core'; import { connect } from "react-redux";
import Validator from "validator";
import {FormLabel, FormHelperText,InputLabel, Input,TextField, FormGroup, FormControlLabel, Checkbox, Button, Typography, Link } from '@material-ui/core';
import Visibility from '@material-ui/icons/Visibility';
import VisibilityOff from '@material-ui/icons/VisibilityOff';
import IconButton from '@material-ui/core/IconButton';
import InputAdornment from '@material-ui/core/InputAdornment';
import FormControl from '@material-ui/core/FormControl';
import { withStyles } from '@material-ui/core/styles'; import { withStyles } from '@material-ui/core/styles';
import { registerUserRequest, subscribeToNewsletterRequest } from "../../actions/user"
const styles = theme => ({ const styles = theme => ({
container: { container: {
@@ -20,22 +27,84 @@ const styles = theme => ({
input50: { input50: {
width: '50%', width: '50%',
}, },
checkboxError:{
color:'red'
}
}); });
class Register extends React.Component { class Register extends React.Component {
state = { state = {
prenom:'', data: {
nom:'', prenom: '',
email:'', nom: '',
password:'', email: '',
checkNewsletter:false, password: '',
checkCGU:false checkNewsletter: false,
checkCGU: false,
},
errors: {},
loading: false,
showPassword:false
} }
validate = data => {
const errors = {};
if (!Validator.isEmail(data.email)) errors.email = "Email invalide";
if (!!!data.password) errors.password = "Requis";
if (!!!data.prenom) errors.prenom = "Requis";
if (!!!data.nom) errors.nom = "Requis";
if (!data.checkCGU) errors.checkCGU = "Requis";
return errors;
};
onRegister = e => {
e.preventDefault();
const errors = this.validate(this.state.data);
console.log('onSubmit')
console.log(errors)
this.setState({ errors: errors });
if (Object.keys(errors).length === 0) {
this.setState({ loading: true });
this.props.registerUserRequest(this.state.data)
if (this.state.data.checkNewsletter)
this.props.subscribeToNewsletterRequest({ email: this.state.data.email })
}
};
componentWillReceiveProps(nextProps) {
this.setState({
errors: nextProps.errors,
loading: nextProps.loading
})
}
onChange = e =>
this.setState({
data: { ...this.state.data, [e.target.name]: e.target.value }
});
handleCheckBoxChange = name => event => {
this.setState({data:{ ...this.state.data, [name]: event.target.checked }});
};
handleChangePasswordVisibility = prop => event => {
this.setState({ [prop]: event.target.value });
};
handleClickShowPassword = () => {
this.setState({ showPassword: !this.state.showPassword });
};
render() { render() {
const { classes } = this.props; const { classes } = this.props;
const { data, errors } = this.state;
console.log('render errors');
console.log(errors);
console.log(this.state);
return ( return (
<div className={classes.container}> <div className={classes.container}>
@@ -43,77 +112,86 @@ class Register extends React.Component {
<form className={classes.form}> <form className={classes.form}>
<FormGroup row> <FormGroup row>
<TextField <TextField
value={this.state.prenom} value={data.prenom}
className={classes.input50} className={classes.input50}
onChange={this.onChange}
id="prenom" id="prenom"
name="prenom" name="prenom"
label="Prénom" label="Prénom"
margin="normal" margin="normal"
required required
error={this.state.prenom === ""} error={!!errors.prenom}
helperText={this.state.prenom === "" ? 'Ce champ est vide' : ' '}
/> />
<TextField <TextField
value={this.state.nom} value={data.nom}
className={classes.input50} className={classes.input50}
onChange={this.onChange}
id="nom" id="nom"
name="nom" name="nom"
label="Nom" label="Nom"
margin="normal" margin="normal"
required required
error={this.state.nom === ""} error={!!errors.nom}
helperText={this.state.nom === "" ? 'Ce champ est vide' : ' '}
/> />
</FormGroup> </FormGroup>
<TextField <TextField
value={this.state.email} value={data.email}
id="email" onChange={this.onChange}
id="emailRegister"
name="email" name="email"
label="Email" label="Email"
margin="normal" margin="normal"
type="email" type="email"
required required
error={this.state.email === ""} error={!!errors.email}
helperText={this.state.email === "" ? 'Ce champ est vide' : ' '} autoComplete="username email"
/>
<TextField
value={this.state.password}
id="password"
name="password"
label="Mot de passe"
margin="normal"
type="password"
required
error={this.state.password === ""}
helperText={this.state.password === "" ? 'Ce champ est vide' : ' '}
/> />
<FormControl>
<InputLabel htmlFor="adornment-password">Password *</InputLabel>
<Input
name="password"
id="adornment-password"
type={this.state.showPassword ? 'text' : 'password'}
value={this.state.password}
onChange={this.onChange}
endAdornment={
<InputAdornment position="end">
<IconButton aria-label="Toggle password visibility" onClick={this.handleClickShowPassword}>
{this.state.showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
}
autoComplete="current-password"
error={!!errors.password}
/>
</FormControl>
<FormControlLabel <FormControlLabel
value={this.state.checkNewsletter}
control={ control={
<Checkbox <Checkbox value={data.checkNewsletter}
value="checkedI" onChange={this.handleCheckBoxChange('checkNewsletter')}
/> />
} }
label="Je souhaite recevoir la newsletter de Talents Tube" label="Je souhaite recevoir la newsletter de Talents Tube"
/> />
<FormControlLabel <FormControlLabel
value={this.state.checkCGU} name="checkCGU"
className={!!errors.checkCGU&&classes.checkboxError}
control={ control={
<Checkbox <Checkbox
value="checkedI" error={!!errors.checkCGU }
value={data.checkCGU}
onChange={this.handleCheckBoxChange('checkCGU')}
/> />
} }
label="Je déclare avoir pris connaissance et accepte les termes et les CGU" label="Je déclare avoir pris connaissance et accepte les termes et les CGU*"
required required
error={this.state.checkCGU === false}
helperText={this.state.checkCGU === "" ? 'Ce champ est requis!' : ' '}
/> />
<Button type="submit" variant="outlined" color="primary" onClick={this.onRegister}>
<Button type="submit" variant="outlined" color="primary">
S'inscrire S'inscrire
</Button> </Button>
@@ -123,10 +201,10 @@ class Register extends React.Component {
>Ou</Typography> >Ou</Typography>
</form> </form>
<Button variant="outlined" color="primary"> <Button variant="outlined" color="primary" onClick={this.props.linkedInAuth}>
Connexion avec Linkedin Connexion avec Linkedin
</Button> </Button>
<Button variant="outlined" color="primary"> <Button variant="outlined" color="primary" onClick={this.props.facebookAuth}>
Connexion avec Facebook Connexion avec Facebook
</Button> </Button>
<div className={classes.form}> <div className={classes.form}>
@@ -145,4 +223,11 @@ class Register extends React.Component {
} }
} }
export default withStyles(styles)(Register); function mapStateToProps(state) {
return {
loading: !!state.user.loading,
errors: state.user.errors
};
}
export default withStyles(styles)(connect(mapStateToProps, { registerUserRequest, subscribeToNewsletterRequest })(Register));
+162 -36
View File
@@ -12,9 +12,53 @@ import Validator from "validator";
import Tabs from '@material-ui/core/Tabs'; import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab'; import Tab from '@material-ui/core/Tab';
import Router from 'next/router' import Router from 'next/router'
import Register from '../components/organisms/Register';
import Snackbar from '@material-ui/core/Snackbar';
import SnackbarContent from '@material-ui/core/SnackbarContent';
import { requestLogin, requestOauthLogin,requestLoginFailure } from "../actions/auth"; import { requestLogin, requestOauthLogin,requestLoginFailure } from "../actions/auth";
import { makeStyles } from '@material-ui/core/styles';
import clsx from 'clsx';
import CheckCircleIcon from '@material-ui/icons/CheckCircle';
import IconButton from '@material-ui/core/IconButton';
import ErrorIcon from '@material-ui/icons/Error';
import InfoIcon from '@material-ui/icons/Info';
import CloseIcon from '@material-ui/icons/Close';
import WarningIcon from '@material-ui/icons/Warning';
import { amber, green } from '@material-ui/core/colors';
import _ from "lodash"
const variantIcon = {
success: CheckCircleIcon,
warning: WarningIcon,
error: ErrorIcon,
info: InfoIcon,
};
const useStyles1 = makeStyles(theme => ({
success: {
backgroundColor: green[600],
},
error: {
backgroundColor: theme.palette.error.dark,
},
info: {
backgroundColor: theme.palette.primary.main,
},
warning: {
backgroundColor: amber[700],
},
icon: {
fontSize: 20,
},
iconVariant: {
opacity: 0.9,
marginRight: theme.spacing(1),
},
message: {
display: 'flex',
alignItems: 'center',
},
}));
function TabContainer({ children, dir }) { function TabContainer({ children, dir }) {
return ( return (
<Typography component="div" dir={dir} style={{ padding: 8 * 3 }}> <Typography component="div" dir={dir} style={{ padding: 8 * 3 }}>
@@ -55,13 +99,37 @@ class LoginPage extends React.Component {
}, },
errors: {}, errors: {},
loading: false, loading: false,
tabIndex:0 tabIndex:0,
openSnack:false
}; };
static getInitialProps({query}) {
const { error } = query
console.log("LoginPage getInitialProps")
console.log(error)
console.log("LoginPage getInitialProps")
return { errors: {message:error}, openSnack:!!error };
}
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
console.log("LoginPage componentWillReceiveProps")
console.log(nextProps)
console.log(!!nextProps.errors)
console.log("LoginPage componentWillReceiveProps")
this.setState({ this.setState({
errors: nextProps.errors, errors: nextProps.errors,
loading: nextProps.loading loading: nextProps.loading,
openSnack: _.isEmpty(nextProps.errors)
}) })
console.log("LoginPage componentWillReceiveProps 2")
console.log(nextProps)
console.log("LoginPage componentWillReceiveProps 2")
}
componentDidMount()
{
} }
onChange = e => onChange = e =>
@@ -104,43 +172,55 @@ class LoginPage extends React.Component {
Router.push('/auth/facebook') Router.push('/auth/facebook')
} }
handleChange= (event, newValue) => { handleChange = (event, newValue) => {
console.log('handleChange') console.log('handleChange')
this.setState({ tabIndex: newValue }); this.setState({ tabIndex: newValue });
} }
handleChangeIndex= (index) =>{ handleChangeIndex = (index) => {
console.log('handleChangeIndex') console.log('handleChangeIndex')
this.setState({ tabIndex: index }); this.setState({ tabIndex: index });
} }
handleClose = (event, reason) => {
// if (reason === 'clickaway') {
// return;
// }
this.setState({ openSnack: false });
}
render() { render() {
const { classes } = this.props; const { classes } = this.props;
const { data, errors, loading } = this.state; const { data, errors, loading, openSnack } = this.state;
console.log("render this.state")
console.log(this.state)
console.log("this.state")
return ( return (
<Layout> <Layout>
<div className={classes.container}> <div className={classes.container}>
{!!errors && ( {/* {!!errors && (
<div className="alert alert-danger">{errors.message}</div> <div className="alert alert-danger">{errors && errors.message}</div>
)} )} */}
{loading && ( {loading && (
<div className="alert alert-info"><CircularProgress color="secondary" />Chargement...</div> <div className="alert alert-info"><CircularProgress color="secondary" />Chargement...</div>
)} )}
{!loading && ( {!loading && (
<Fragment> <Fragment>
<AppBar position="static" color="default"> <AppBar position="static" color="default">
<Tabs <Tabs
value={this.state.tabIndex} value={this.state.tabIndex}
onChange={this.handleChange} onChange={this.handleChange}
indicatorColor="primary" indicatorColor="primary"
textColor="primary" textColor="primary"
variant="fullWidth" variant="fullWidth"
> >
<Tab label="Connexion" /> <Tab label="Connexion" />
<Tab label="Inscription" /> <Tab label="Inscription" />
</Tabs> </Tabs>
</AppBar> </AppBar>
<SwipeableViews <SwipeableViews
axis={ 'x'} axis={ 'x'}
index={this.state.tabIndex} index={this.state.tabIndex}
@@ -160,8 +240,8 @@ class LoginPage extends React.Component {
onChange={this.onChange} onChange={this.onChange}
autoComplete="username" autoComplete="username"
required required
error={data.email === ""} // error={data.email === ""}
helperText={data.email === "" ? 'Ce champ est vide' : ' '} // helperText={data.email === "" ? 'Ce champ est vide' : ' '}
/> />
<TextField <TextField
@@ -174,8 +254,8 @@ class LoginPage extends React.Component {
value={data.password} value={data.password}
onChange={this.onChange} onChange={this.onChange}
autoComplete="current-password" autoComplete="current-password"
error={data.password === ""} // error={data.password === ""}
helperText={data.password === "" ? 'Ce champ est vide' : ' '} // helperText={data.password === "" ? 'Ce champ est vide' : ' '}
required required
/> />
@@ -208,32 +288,78 @@ class LoginPage extends React.Component {
<Typography <Typography
paragraph={true} paragraph={true}
align="center" align="center"
>Je ne possède pas de compte Talents Tube ? >Je ne possède pas de compte Talents Tube ?
<Link href={"/Register"} className={classes.link}> <span onClick={() => this.handleChangeIndex(1)} className={classes.link}>
S'inscrire S'inscrire
</Link> </span>
</Typography> </Typography>
</div> </div>
<div className={classes.form}><Typography
paragraph={true}
align="center"
>{this.state.openSnack}
</Typography></div>
</Fragment> </Fragment>
</TabContainer> </TabContainer>
<TabContainer dir={'x'}>Item Two</TabContainer> <TabContainer dir={'x'}>
</SwipeableViews> <Register linkedInAuth={this.linkedInAuth} facebookAuth={this.facebookAuth} />
</Fragment> </TabContainer>
</SwipeableViews>
</Fragment>
)} )}
</div> </div>
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right',
}}
open={openSnack}
autoHideDuration={6000}
>
<MySnackbarContentWrapper
variant="error"
className={classes.margin}
message={errors.message}
onClose={this.handleClose} />
</Snackbar>
</Layout> </Layout>
); );
} }
} }
function mapStateToProps(state) { function mapStateToProps(state,ownProps) {
return { return {
openSnack:!_.isEmpty(ownProps.errors),
loading: !!state.user.loading, loading: !!state.user.loading,
errors: state.user.errors errors: state.user.errors || ownProps.errors
}; };
} }
function MySnackbarContentWrapper(props) {
const classes = useStyles1();
const { className, message, onClose, variant, ...other } = props;
const Icon = variantIcon[variant];
return (
<SnackbarContent
className={clsx(classes[variant], className)}
aria-describedby="client-snackbar"
message={
<span id="client-snackbar" className={classes.message}>
<Icon className={clsx(classes.icon, classes.iconVariant)} />
{message}
</span>
}
action={[
<IconButton key="close" aria-label="Close" color="inherit" onClick={onClose}>
<CloseIcon className={classes.icon} />
</IconButton>,
]}
{...other}
/>
);
}
export default withStyles(styles)(connect(mapStateToProps, { requestLogin, requestOauthLogin, requestLoginFailure })(LoginPage)); export default withStyles(styles)(connect(mapStateToProps, { requestLogin, requestOauthLogin, requestLoginFailure })(LoginPage));
+11
View File
@@ -0,0 +1,11 @@
import React, { Component } from 'react'
export default class VerifyEmail extends Component {
render() {
return (
<div>
Vérifiez vos email pour confirmer votre inscription.
</div>
)
}
}
-2
View File
@@ -104,8 +104,6 @@ class Index extends React.Component {
<HorizontalAdScroller data={this.state.promotedAds} /> <HorizontalAdScroller data={this.state.promotedAds} />
<Button variant="outlined" onClick={() => Router.push('/offres')}>Toutes les offres</Button> <Button variant="outlined" onClick={() => Router.push('/offres')}>Toutes les offres</Button>
<HorizontalCompanyScroller data={this.state.promotedAds} /> <HorizontalCompanyScroller data={this.state.promotedAds} />
<Button variant="outlined" onClick={() => Router.push('/companies')}>Toutes les entreprises</Button> <Button variant="outlined" onClick={() => Router.push('/companies')}>Toutes les entreprises</Button>
+1 -1
View File
@@ -42,7 +42,7 @@ export function user(state = {
case REQUEST_LOGIN_FAILURE: case REQUEST_LOGIN_FAILURE:
return { return {
loading: false, loading: false,
errors: { ...action.data.message }, errors: { ...action.message },
} }
case FETCH_CURRENT_USER_REQUEST: case FETCH_CURRENT_USER_REQUEST:
return { return {
+3 -1
View File
@@ -3,13 +3,14 @@
import { all, call, delay, put, take, takeLatest } from 'redux-saga/effects' import { all, call, delay, put, take, takeLatest } from 'redux-saga/effects'
import es6promise from 'es6-promise' import es6promise from 'es6-promise'
import { requestLoginSaga, requestUserSaga, requestLogoutSaga, requestNewsletterSubscribitionSaga} from './sagas/userSaga.js' import { requestLoginSaga, requestUserSaga, requestRegisterSaga, requestLogoutSaga, requestNewsletterSubscribitionSaga} from './sagas/userSaga.js'
import { requestAdsSaga, requestAdSaga, requestPromotedAdsSaga } from './sagas/offreSaga.js' import { requestAdsSaga, requestAdSaga, requestPromotedAdsSaga } from './sagas/offreSaga.js'
import {requestCompaniesSaga} from './sagas/companySaga.js' import {requestCompaniesSaga} from './sagas/companySaga.js'
import { import {
REQUEST_LOGIN, REQUEST_LOGIN,
REQUEST_LOGOUT, REQUEST_LOGOUT,
REQUEST_REGISTER,
FETCH_CURRENT_USER_REQUEST, FETCH_CURRENT_USER_REQUEST,
FETCH_ADS_REQUEST, FETCH_ADS_REQUEST,
FETCH_AD_REQUEST, FETCH_AD_REQUEST,
@@ -26,6 +27,7 @@ function* rootSaga() {
yield all([ yield all([
takeLatest(REQUEST_LOGIN, requestLoginSaga), takeLatest(REQUEST_LOGIN, requestLoginSaga),
takeLatest(REQUEST_LOGOUT, requestLogoutSaga), takeLatest(REQUEST_LOGOUT, requestLogoutSaga),
takeLatest(REQUEST_REGISTER, requestRegisterSaga),
takeLatest(FETCH_CURRENT_USER_REQUEST, requestUserSaga), takeLatest(FETCH_CURRENT_USER_REQUEST, requestUserSaga),
takeLatest(SUBSCRIBE_NEWSLETTER_REQUEST, requestNewsletterSubscribitionSaga), takeLatest(SUBSCRIBE_NEWSLETTER_REQUEST, requestNewsletterSubscribitionSaga),
takeLatest(FETCH_ADS_REQUEST, requestAdsSaga), takeLatest(FETCH_ADS_REQUEST, requestAdsSaga),
+7 -3
View File
@@ -1,8 +1,11 @@
import { call, put } from "redux-saga/effects"; import { call, put } from "redux-saga/effects";
import { userLoggedIn, requestLoginFailure,userLoggedOut } from "../actions/auth"; import { userLoggedIn, requestLoginFailure, userLoggedOut } from "../actions/auth";
import { fetchCurrentUserSuccess, fetchCurrentUserFailure, import {
subscribeToNewsletterSuccess,subscribeToNewsletterFailure } from "../actions/user"; fetchCurrentUserSuccess, fetchCurrentUserFailure,
subscribeToNewsletterSuccess, subscribeToNewsletterFailure,
registerUserSuccess, registerUserFailure
} from "../actions/user";
import setAuthorizationHeader from "../data/setAuthorizationHeader"; import setAuthorizationHeader from "../data/setAuthorizationHeader";
import api from "../data/api"; import api from "../data/api";
@@ -100,6 +103,7 @@ export function* requestRegisterSaga(action) {
if (res.data.success === true) { if (res.data.success === true) {
yield put(registerUserSuccess(res)); yield put(registerUserSuccess(res));
Router.push('/VerifyEmail');
} else } else
throw new Error("not subscribed") throw new Error("not subscribed")
+9 -4
View File
@@ -70,12 +70,13 @@ app.prepare()
server.use(passport.initialize()); server.use(passport.initialize());
server.get('/auth/facebook', server.get('/auth/facebook',
passport.authorize('facebook', { scope: ['email', 'public_profile'], session: false })); passport.authorize('facebook', { scope: ['email', 'public_profile'], session: false, failureRedirect: '/login'}));
server.get('/auth/facebook/callback', server.get('/auth/facebook/callback',
passport.authorize('facebook', { failureRedirect: '/login', session: false }), passport.authorize('facebook', { failureRedirect: '/login', session: false }),
(req, res) => { (req, res) => {
// Successful authentication, redirect home.
console.log('facebookcallback') console.log('facebookcallback')
console.log(req.account)//don't understand why ! console.log(req.account)//don't understand why !
// console.log(req.accessToken) // console.log(req.accessToken)
@@ -91,11 +92,15 @@ app.prepare()
}) })
server.get('/auth/linkedin', server.get('/auth/linkedin',
passport.authorize('linkedin', { scope: ['r_basicprofile', 'r_emailaddress', 'r_liteprofile'], session: false })); passport.authorize('linkedin', { scope: ['r_basicprofile', 'r_emailaddress', 'r_liteprofile'], session: false , failureRedirect: '/login'}));
server.get('/auth/linkedin/callback', server.get('/auth/linkedin/callback',
passport.authorize('linkedin', { failureRedirect: '/login', session: false }), passport.authorize('linkedin', { failureRedirect: '/login', session: false }),
function (req, res) { function (error, req, res, next) {
if (error) {
console.log(error)
return res.redirect('/login?error='+error.message)
}
// Successful authentication, redirect home. // Successful authentication, redirect home.
console.log('linkedincallback') console.log('linkedincallback')
console.log(req.accessToken) console.log(req.accessToken)
-1
View File
@@ -119,7 +119,6 @@ class parametres extends Component {
const topicsTemp = cloneDeep(this.state.topics) const topicsTemp = cloneDeep(this.state.topics)
topicsTemp.map(temp => topicsTemp.map(temp =>
temp.value = !!topics[temp.key] temp.value = !!topics[temp.key]
) )
this.setState({ topics: topicsTemp }) this.setState({ topics: topicsTemp })
} }