Complete forgot password page.

This commit is contained in:
Paulo Gustavo Veiga 2020-12-11 20:06:42 -08:00
parent bc3b7104dd
commit bec0c7998c
10 changed files with 134 additions and 84 deletions

View File

@ -2,9 +2,10 @@ import React, { useEffect, useState } from 'react';
import { Service, RestService } from './services/Service'; import { Service, RestService } from './services/Service';
import { IntlProvider } from 'react-intl' import { IntlProvider } from 'react-intl'
import {GlobalStyle} from './theme/global-style'; import { GlobalStyle } from './theme/global-style';
import { RegistrationSuccessPage } from './components/registration-success-page'; import RegistrationSuccessPage from './components/registration-success-page';
import { RegistationPage } from './components/registration-page'; import ForgotPasswordSuccessPage from './components/forgot-password-success-page';
import RegistationPage from './components/registration-page';
import LoginPage from './components/login-page'; import LoginPage from './components/login-page';
import { import {
@ -49,7 +50,7 @@ const App = () => {
return messages ? ( return messages ? (
<IntlProvider locale={locale} defaultLocale='en' messages={messages}> <IntlProvider locale={locale} defaultLocale='en' messages={messages}>
<GlobalStyle/> <GlobalStyle />
<Router> <Router>
<Switch> <Switch>
<Route exact path="/"> <Route exact path="/">
@ -60,7 +61,10 @@ const App = () => {
<RegistationPage service={service} /> <RegistationPage service={service} />
</Route> </Route>
<Route path="/c/user/registrationSuccess" component={RegistrationSuccessPage} /> <Route path="/c/user/registrationSuccess" component={RegistrationSuccessPage} />
<Route path="/c/user/resetPassword" component={ForgotPasswordPage} /> <Route path="/c/user/resetPassword">
<ForgotPasswordPage service={service} />
</Route>
<Route path="/c/user/forgotPasswordSuccess" component={ForgotPasswordSuccessPage} />
</Switch> </Switch>
</Router> </Router>
</IntlProvider> </IntlProvider>

View File

@ -6,25 +6,17 @@ import { Service } from '../../services/Service'
import { PageContent } from '../../theme/global-style'; import { PageContent } from '../../theme/global-style';
import Header from '../header' import Header from '../header'
import Footer from '../footer' import Footer from '../footer'
import FormErrorDialog from '../form-error-dialog'
import SubmitButton from '../submit-button' import SubmitButton from '../submit-button'
interface ErrorMessageDialogProps {
message: string
}
const ErrorMessageDialog = (props: ErrorMessageDialogProps) => {
const message = props.message;
return message ? <p className='form-error-dialog'>{message}</p> : <span></span>;
}
type ForgotPasswordProps = { type ForgotPasswordProps = {
email: string; email: string;
} }
const RegistrationForm = (props: ServiceProps) => { const ForgotPassword = (props: ServiceProps) => {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [errorMsg, setErrorMsg] = useState(""); const [errorMsg, setErrorMsg] = useState("");
@ -37,7 +29,6 @@ const RegistrationForm = (props: ServiceProps) => {
event.preventDefault(); event.preventDefault();
setDisableButton(true); setDisableButton(true);
// Call Service ... // Call Service ...
props.service.resetPassword( props.service.resetPassword(
email, email,
@ -51,10 +42,10 @@ const RegistrationForm = (props: ServiceProps) => {
<h1><FormattedMessage id="forgot.title" defaultMessage="Reset your password" /></h1> <h1><FormattedMessage id="forgot.title" defaultMessage="Reset your password" /></h1>
<p><FormattedMessage id="forgot.desc" defaultMessage="We will send you an email to reset your password" /></p> <p><FormattedMessage id="forgot.desc" defaultMessage="We will send you an email to reset your password" /></p>
<form method="POST" onSubmit={e => handleSubmit(e)}> <form onSubmit={e => handleSubmit(e)}>
<input type="email" name="email" onChange={e => setEmail(e.target.value)} placeholder={intl.formatMessage({ id: "forgot.email", defaultMessage: "Email" })} required={true} autoComplete="email" /> <input type="email" name="email" onChange={e => setEmail(e.target.value)} placeholder={intl.formatMessage({ id: "forgot.email", defaultMessage: "Email" })} required={true} autoComplete="email" />
<ErrorMessageDialog message={errorMsg} /> <FormErrorDialog message={errorMsg} />
<SubmitButton disabled={disableButton} value={intl.formatMessage({ id: "forgot.register", defaultMessage: "Send recovery link" })} /> <SubmitButton disabled={disableButton} value={intl.formatMessage({ id: "forgot.register", defaultMessage: "Send recovery link" })} />
</form> </form>
@ -68,13 +59,13 @@ type ServiceProps = {
const ForgotPasswordPage = (props: ServiceProps) => { const ForgotPasswordPage = (props: ServiceProps) => {
useEffect(() => { useEffect(() => {
document.title = 'Forgot Password | WiseMapping'; document.title = 'Reset Password | WiseMapping';
}); });
return ( return (
<div> <div>
<Header type='only-signin' /> <Header type='only-signin' />
<RegistrationForm service={props.service} /> <ForgotPassword service={props.service} />
<Footer /> <Footer />
</div> </div>
); );

View File

@ -0,0 +1,36 @@
import React, { useEffect } from 'react'
import { FormattedMessage } from 'react-intl'
import {PageContent} from '../../theme/global-style';
import Header, { SignInButton } from '../header'
import Footer from '../footer'
const ForgotPasswordSuccessPage = () => {
useEffect(() => {
document.title = 'Reset Password | WiseMapping';
});
return (
<div>
<Header type='none' />
<div>
<PageContent>
<h1>
<FormattedMessage id="forgot.success.title" defaultMessage="Your temporal password has been sent" />
</h1>
<p>
<FormattedMessage id="forgot.success.desc" defaultMessage="We've sent you an email that will allow you to reset your password. Please check your email now." />
</p>
<SignInButton style='style1' />
</PageContent>
</div>
<Footer />
</div>
);
}
export default ForgotPasswordSuccessPage

View File

@ -1,4 +1,4 @@
import React, { useEffect } from 'react' import React from 'react'
import { StyleDiv } from './styled' import { StyleDiv } from './styled'
type FormErrorDialogProps = { type FormErrorDialogProps = {

View File

@ -51,7 +51,7 @@ const RegistrationForm = (props: ServiceProps) => {
<h1><FormattedMessage id="registration.title" defaultMessage="Become a member of our comunity" /></h1> <h1><FormattedMessage id="registration.title" defaultMessage="Become a member of our comunity" /></h1>
<p><FormattedMessage id="registration.desc" defaultMessage="Signing up is free and just take a moment " /></p> <p><FormattedMessage id="registration.desc" defaultMessage="Signing up is free and just take a moment " /></p>
<form method="POST" onSubmit={e => handleSubmit(e)}> <form onSubmit={e => handleSubmit(e)}>
<input type="email" name="email" onChange={e => setEmail(e.target.value)} placeholder={intl.formatMessage({ id: "registration.email", defaultMessage: "Email" })} required={true} autoComplete="email" /> <input type="email" name="email" onChange={e => setEmail(e.target.value)} placeholder={intl.formatMessage({ id: "registration.email", defaultMessage: "Email" })} required={true} autoComplete="email" />
<input type="text" name="firstname" onChange={e => setFirstname(e.target.value)} placeholder={intl.formatMessage({ id: "registration.firstname", defaultMessage: "First Name" })} required={true} autoComplete="given-name" /> <input type="text" name="firstname" onChange={e => setFirstname(e.target.value)} placeholder={intl.formatMessage({ id: "registration.firstname", defaultMessage: "First Name" })} required={true} autoComplete="given-name" />
<input type="text" name="lastname" onChange={e => setLastname(e.target.value)} placeholder={intl.formatMessage({ id: "registration.lastname", defaultMessage: "Last Name" })} required={true} autoComplete="family-name" /> <input type="text" name="lastname" onChange={e => setLastname(e.target.value)} placeholder={intl.formatMessage({ id: "registration.lastname", defaultMessage: "Last Name" })} required={true} autoComplete="family-name" />
@ -93,6 +93,6 @@ const RegistationPage = (props: ServiceProps) => {
); );
} }
export { RegistationPage } export default RegistationPage;

View File

@ -10,7 +10,7 @@ import Footer from '../footer'
const RegistrationSuccessPage = () => { const RegistrationSuccessPage = () => {
useEffect(() => { useEffect(() => {
document.title = 'Registration | WiseMapping'; document.title = 'Reset Password | WiseMapping';
}); });
return ( return (
@ -19,7 +19,7 @@ const RegistrationSuccessPage = () => {
<div> <div>
<PageContent> <PageContent>
<h1> <h1>
<FormattedMessage id="registration.success.title" defaultMessage="Your account has been created successfully" /> <FormattedMessage id="resetpassword.success.title" defaultMessage="Your account has been created successfully" />
</h1> </h1>
<p> <p>
<FormattedMessage id="registration.success.desc" defaultMessage="Click 'Sign In' button below and start creating mind maps." /> <FormattedMessage id="registration.success.desc" defaultMessage="Click 'Sign In' button below and start creating mind maps." />
@ -33,6 +33,6 @@ const RegistrationSuccessPage = () => {
); );
} }
export { RegistrationSuccessPage } export default RegistrationSuccessPage;

View File

@ -9,18 +9,11 @@ const SubmitButton = (props: SubmitButton) => {
const [disabled, setDisabled] = useState(props.disabled ? true : false); const [disabled, setDisabled] = useState(props.disabled ? true : false);
const intl = useIntl(); const intl = useIntl();
useEffect(() => {
document.title = 'WiseMapping - Login';
});
let valueTxt = props.value; let valueTxt = props.value;
if (disabled) { if (disabled) {
valueTxt = intl.formatMessage({ id: "common.wait", defaultMessage: "Please wait ..." }); valueTxt = intl.formatMessage({ id: "common.wait", defaultMessage: "Please wait ..." });
} }
const [value, setValue] = useState(valueTxt); const [value, setValue] = useState(valueTxt);
console.log(disabled);
console.log(value);
return ( return (
<input type="submit" disabled={disabled} value={value} /> <input type="submit" disabled={disabled} value={value} />
); );

View File

@ -6,12 +6,12 @@ type NewUser = {
lastname: string; lastname: string;
password: string; password: string;
recaptcha: string | null; recaptcha: string | null;
} }
interface Service { interface Service {
registerNewUser(user: NewUser, onSuccess: () => void, onError: (msg: string) => void): void; registerNewUser(user: NewUser, onSuccess: () => void, onError: (msg: string) => void): void;
resetPassword(email:string, onSuccess: () => void, onError: (msg: string) => void): void; resetPassword(email: string, onSuccess: () => void, onError: (msg: string) => void): void;
} }
class RestService implements Service { class RestService implements Service {
@ -24,7 +24,7 @@ class RestService implements Service {
async registerNewUser(user: NewUser, onSuccess: () => void, onError: (msg: string) => void) { async registerNewUser(user: NewUser, onSuccess: () => void, onError: (msg: string) => void) {
await axios.post(this.baseUrl + '/service/user', await axios.post(this.baseUrl + '/service/users',
JSON.stringify(user), JSON.stringify(user),
{ headers: { 'Content-Type': 'application/json' } } { headers: { 'Content-Type': 'application/json' } }
).then(response => { ).then(response => {
@ -32,36 +32,61 @@ class RestService implements Service {
onSuccess(); onSuccess();
}).catch(error => { }).catch(error => {
const response = error.response; const response = error.response;
let msg = ''; const errorMsg = this.parseResponseOnError(response);
onError(errorMsg);
});
}
async resetPassword(email: string, onSuccess: () => void, onError: (msg: string) => void) {
await axios.put(this.baseUrl + '/service/users/resetPassword?email=' + email,
null,
{ headers: { 'Content-Type': 'application/json' } }
).then(response => {
// All was ok, let's sent to success page ...
onSuccess();
}).catch(error => {
const response = error.response;
const errorMsg = this.parseResponseOnError(response);
onError(errorMsg);
});
}
private parseResponseOnError = (response: any) => {
let msg;
if (response) { if (response) {
const status: number = response.status; const status: number = response.status;
const data = response.data; const data = response.data;
console.log(data);
switch (status) { switch (status) {
case 401: case 401:
this.authFailed(); this.authFailed();
break; break;
default: default:
console.log(data); if (data) {
// Is a server error ? let errors: string[] = [];
if (!data.fieldErrors) { if (data.globalErrors) {
msg = response.statusText; errors = data.globalErrors;
} else if (data) { } else if (data.fieldErrors) {
const fieldsError = data.fieldErrors; errors = Object.values(data.fieldErrors);
msg = Object.values(fieldsError)[0] as string;
} }
if (errors.length > 0) {
msg = errors[0];
} }
} else { } else {
msg = response.statusText;
}
}
}
// Network related problem ... // Network related problem ...
if (!msg) {
msg = 'Unexpected error. Please, try latter'; msg = 'Unexpected error. Please, try latter';
} }
onError(msg);
});
}
resetPassword(email:string, onSuccess: () => void, onError: (msg: string) => void): void {
return msg;
} }
} }

View File

@ -124,6 +124,10 @@ padding: 20px 10px 20px 10px;
background-color: #f9a826; background-color: #f9a826;
} }
& label {
font-size:15px;
}
& input:placeholder { & input:placeholder {
color: grey; color: grey;
} }
@ -147,7 +151,7 @@ padding: 20px 10px 20px 10px;
} }
& a { & a {
font-size: 17px; font-size: 15px;
color: #f9a826; color: #f9a826;
} }
`; `;

View File

@ -1,6 +1,5 @@
const path = require('path'); const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackDynamicEnvPlugin = require('html-webpack-dynamic-env-plugin');
const webpack = require('webpack'); const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin');
@ -17,13 +16,11 @@ module.exports = {
extensions: ['.ts', '.tsx', '.js', '.jsx'] extensions: ['.ts', '.tsx', '.js', '.jsx']
}, },
module: { module: {
rules: [ rules: [{
{
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: 'ts-loader',
exclude: '/node_modules/' exclude: '/node_modules/'
}, , },
{ {
test: /\.(png|jpe?g|gif|svg)$/, test: /\.(png|jpe?g|gif|svg)$/,
use: [{ use: [{
@ -31,7 +28,7 @@ module.exports = {
options: { options: {
esModule: false, esModule: false,
} }
}, ], }]
} }
] ]
}, },