Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/volto/news/7697.internal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Refactored the `PasswordReset` widget by converting it from a class-based component to a modern functional component using React hooks. @Manik-Khajuria-5
303 changes: 112 additions & 191 deletions packages/volto/src/components/theme/PasswordReset/PasswordReset.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
* @module components/theme/PasswordReset/PasswordReset
*/

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { Link, withRouter } from 'react-router-dom';
import { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Link, useHistory, useParams } from 'react-router-dom';
import Helmet from '@plone/volto/helpers/Helmet/Helmet';
import { Container } from 'semantic-ui-react';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';

import { Form } from '@plone/volto/components/manage/Form';
import { setInitialPassword } from '@plone/volto/actions/users/users';
Expand Down Expand Up @@ -90,220 +88,143 @@ const messages = defineMessages({
});

/**
* PasswordReset class.
* @class PasswordReset
* @extends Component
* @function PasswordReset
* @returns {JSX.Element}
*/
class PasswordReset extends Component {
/**
* Property types.
* @property {Object} propTypes Property types.
* @static
*/
static propTypes = {
loading: PropTypes.bool.isRequired,
loaded: PropTypes.bool.isRequired,
error: PropTypes.string,
token: PropTypes.string.isRequired,
setInitialPassword: PropTypes.func.isRequired,
};
function PasswordReset() {
const dispatch = useDispatch();
const history = useHistory();
const { token } = useParams();

/**
* Default properties.
* @property {Object} defaultProps Default properties.
* @static
*/
static defaultProps = {
error: null,
};
const loading = useSelector((state) => state.users.initial.loading);
const loaded = useSelector((state) => state.users.initial.loaded);
const error = useSelector((state) => state.users.initial.error);

/**
* Constructor
* @method constructor
* @param {Object} props Component properties
* @constructs Controlpanel
*/
constructor(props) {
super(props);
this.onCancel = this.onCancel.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
error: null,
isSuccessful: false,
};
const [localError, setLocalError] = useState(null);
const [isSuccessful, setIsSuccessful] = useState(false);
const intl = useIntl();

this.identifierField = config.settings.useEmailAsLogin
? 'email'
: 'username';
const identifierField = config.settings.useEmailAsLogin
? 'email'
: 'username';

this.identifierTitle =
this.identifierField === 'email'
? this.props.intl.formatMessage(messages.emailTitle)
: this.props.intl.formatMessage(messages.usernameTitle);
const identifierTitle =
identifierField === 'email'
? intl.formatMessage(messages.emailTitle)
: intl.formatMessage(messages.usernameTitle);

this.identifierDescription =
this.identifierField === 'email'
? this.props.intl.formatMessage(messages.emailDescription)
: this.props.intl.formatMessage(messages.usernameDescription);
}
const identifierDescription =
identifierField === 'email'
? intl.formatMessage(messages.emailDescription)
: intl.formatMessage(messages.usernameDescription);

/**
* Component will receive props
* @method componentWillReceiveProps
* @param {Object} nextProps Next properties
* @returns {undefined}
*/
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.props.loading && nextProps.loaded) {
this.setState({ isSuccessful: true });
useEffect(() => {
if (!loading && loaded) {
setIsSuccessful(true);
}
}
}, [loading, loaded]);

/**
* Submit handler
* @method onSubmit
* @param {object} data Form data.
* @param {object} event Form data.
* @returns {undefined}
*/
onSubmit(data) {
const onSubmit = (data) => {
if (data.password === data.passwordRepeat) {
this.props.setInitialPassword(
data[this.identifierField],
this.props.token,
data.password,
);
this.setState({
error: null,
});
dispatch(setInitialPassword(data[identifierField], token, data.password));
setLocalError(null);
} else {
this.setState({
error: {
message: this.props.intl.formatMessage(messages.passwordsDoNotMatch),
},
setLocalError({
message: intl.formatMessage(messages.passwordsDoNotMatch),
});
}
}
};

/**
* Cancel handler
* @method onCancel
* @returns {undefined}
*/
onCancel() {
this.props.history.goBack();
const onCancel = () => {
history.goBack();
};

if (isSuccessful) {
return (
<Container>
<h1 className="documentFirstHeading">
<FormattedMessage
id="Account activation completed"
defaultMessage="Account activation completed"
/>
</h1>
<p className="description">
<FormattedMessage
id="Your password has been set successfully. You may now {link} with your new password."
defaultMessage="Your password has been set successfully. You may now {link} with your new password."
values={{
link: (
<Link to="/login">{intl.formatMessage({ id: 'Log In' })}</Link>
),
}}
/>
</p>
</Container>
);
}

/**
* Render method.
* @method render
* @returns {string} Markup for the component.
*/
render() {
if (this.state.isSuccessful) {
return (
if (token) {
const errmsg = error ? error.response?.body?.error || error : null;
return (
<div id="page-password-reset">
<Helmet title={intl.formatMessage(messages.passwordReset)} />
<Container>
<h1 className="documentFirstHeading">
<FormattedMessage
id="Account activation completed"
defaultMessage="Account activation completed"
/>
</h1>
<p className="description">
<FormattedMessage
id="Your password has been set successfully. You may now {link} with your new password."
defaultMessage="Your password has been set successfully. You may now {link} with your new password."
values={{
link: (
<Link to="/login">
{this.props.intl.formatMessage({ id: 'Log In' })}
</Link>
),
}}
/>
</p>
</Container>
);
}
if (this.props.token) {
const errmsg = this.props.error
? this.props.error.response.body.error
: null;
return (
<div id="page-password-reset">
<Helmet
title={this.props.intl.formatMessage(messages.passwordReset)}
/>
<Container>
<Form
title={this.props.intl.formatMessage(messages.title)}
description={this.props.intl.formatMessage(messages.description)}
onSubmit={this.onSubmit}
onCancel={this.onCancel}
error={this.state.error || errmsg}
schema={{
fieldsets: [
{
id: 'default',
title: this.props.intl.formatMessage(messages.default),
fields: [
this.identifierField,
'password',
'passwordRepeat',
],
},
],
properties: {
[this.identifierField]: {
type: 'string',
title: this.identifierTitle,
description: this.identifierDescription,
},
password: {
description: this.props.intl.formatMessage(
messages.passwordDescription,
),
title: this.props.intl.formatMessage(
messages.passwordTitle,
),
type: 'string',
widget: 'password',
},
passwordRepeat: {
description: this.props.intl.formatMessage(
messages.passwordRepeatDescription,
),
title: this.props.intl.formatMessage(
messages.passwordRepeatTitle,
),
type: 'string',
widget: 'password',
},
<Form
title={intl.formatMessage(messages.title)}
description={intl.formatMessage(messages.description)}
onSubmit={onSubmit}
onCancel={onCancel}
error={localError || errmsg}
schema={{
fieldsets: [
{
id: 'default',
title: intl.formatMessage(messages.default),
fields: [identifierField, 'password', 'passwordRepeat'],
},
submitLabel: this.props.intl.formatMessage(
messages.setMyPassword,
),
required: [this.identifierField, 'password', 'passwordRepeat'],
}}
/>
</Container>
</div>
);
}
return <div />;
],
properties: {
[identifierField]: {
type: 'string',
title: identifierTitle,
description: identifierDescription,
},
password: {
description: intl.formatMessage(messages.passwordDescription),
title: intl.formatMessage(messages.passwordTitle),
type: 'string',
widget: 'password',
},
passwordRepeat: {
description: intl.formatMessage(
messages.passwordRepeatDescription,
),
title: intl.formatMessage(messages.passwordRepeatTitle),
type: 'string',
widget: 'password',
},
},
submitLabel: intl.formatMessage(messages.setMyPassword),
required: [identifierField, 'password', 'passwordRepeat'],
}}
/>
</Container>
</div>
);
}

return <div />;
}

export default compose(
withRouter,
injectIntl,
connect(
(state, props) => ({
loading: state.users.initial.loading,
loaded: state.users.initial.loaded,
error: state.users.initial.error,
token: props.match.params.token,
}),
{ setInitialPassword },
),
)(PasswordReset);
export default PasswordReset;