Skip to content

Commit

Permalink
Add send password reset behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrieldutra committed Jan 31, 2019
1 parent 576c3d0 commit d891df1
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 44 deletions.
1 change: 1 addition & 0 deletions client/app/assets/less/ant.less
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@import '~antd/lib/style/core/iconfont';
@import '~antd/lib/style/core/motion';
@import '~antd/lib/alert/style/index';
@import '~antd/lib/input/style/index';
@import '~antd/lib/input-number/style/index';
@import '~antd/lib/date-picker/style/index';
Expand Down
2 changes: 1 addition & 1 deletion client/app/components/dynamic-form/DynamicForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export const DynamicForm = Form.create()(class DynamicForm extends React.Compone
htmlType: 'button',
className: action.pullRight ? 'pull-right m-t-10' : 'm-t-10',
type: action.type,
disabled: inProgress || (isFieldsTouched() && action.disableWhenDirty),
disabled: (isFieldsTouched() && action.disableWhenDirty),
loading: inProgress,
onClick: this.handleAction,
};
Expand Down
70 changes: 58 additions & 12 deletions client/app/components/users/UserEdit.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React from 'react';
import Alert from 'antd/lib/alert';
import Button from 'antd/lib/button';
import Icon from 'antd/lib/icon';
import Input from 'antd/lib/input';
import Tooltip from 'antd/lib/tooltip';
import Modal from 'antd/lib/modal';
import { react2angular } from 'react2angular';
import { User } from '@/services/user';
import { currentUser } from '@/services/auth';
import { currentUser, clientConfig } from '@/services/auth';
import { absoluteUrl } from '@/services/utils';
import { UserProfile } from '../proptypes';
import { DynamicForm } from '../dynamic-form/DynamicForm';

Expand All @@ -17,7 +19,11 @@ export class UserEdit extends React.Component {

constructor(props) {
super(props);
this.state = { user: this.props.user, changePassword: false };
this.state = {
user: this.props.user,
changingPassword: false,
sendingPasswordEmail: false,
};
}

handleSave = (values, onSuccess, onError) => {
Expand All @@ -44,7 +50,18 @@ export class UserEdit extends React.Component {
};

openChangePasswordModal = () => {
this.setState({ changePassword: true });
this.setState({ changingPassword: true });
};

sendPasswordReset = () => {
const { user } = this.state;
this.setState({ sendingPasswordEmail: true });

User.sendPasswordReset(user).then((passwordResetLink) => {
this.setState({ passwordResetLink });
}).finally(() => {
this.setState({ sendingPasswordEmail: false });
});
};

changePasswordModal() {
Expand All @@ -56,9 +73,9 @@ export class UserEdit extends React.Component {

return (
<Modal
visible={this.state.changePassword}
visible={this.state.changingPassword}
title="Change Password"
onCancel={() => { this.setState({ changePassword: false }); }}
onCancel={() => { this.setState({ changingPassword: false }); }}
footer={null}
destroyOnClose
>
Expand All @@ -69,11 +86,9 @@ export class UserEdit extends React.Component {

regenerateApiKey = () => {
const doRegenerate = () => {
User.regenerateApiKey(this.state.user).then(({ data }) => {
if (data) {
const { user } = this.state;
this.setState({ user: { ...user, apiKey: data.api_key } });
}
User.regenerateApiKey(this.state.user).then((apiKey) => {
const { user } = this.state;
this.setState({ user: { ...user, apiKey } });
});
};

Expand Down Expand Up @@ -105,6 +120,37 @@ export class UserEdit extends React.Component {
);
}

renderPasswordReset() {
const { user, sendingPasswordEmail, passwordResetLink } = this.state;

return (
<React.Fragment>
<Button
className="w-100 m-t-10"
onClick={this.sendPasswordReset}
loading={sendingPasswordEmail}
>
Send Password Reset Email
</Button>
{passwordResetLink &&
<Alert
message={clientConfig.mailSettingsMissing ? (
<p>
The mail server is not configured, please send the following link
to {user.name} to reset their password:
<Input.TextArea value={absoluteUrl(passwordResetLink)} readOnly />
</p>
) : 'The user should receive a link to reset their password by email soon.'}
type="success"
className="m-t-20"
afterClose={() => { this.setState({ passwordResetLink: null }); }}
closable
/>
}
</React.Fragment>
);
}

render() {
const { user } = this.state;

Expand Down Expand Up @@ -136,13 +182,13 @@ export class UserEdit extends React.Component {
<h3 className="profile__h3">{user.name}</h3>
<hr />
<DynamicForm fields={formFields} readOnly={user.isDisabled} onSubmit={this.handleSave} />
{this.changePasswordModal()}
{!user.isDisabled && (
<React.Fragment>
{this.renderApiKey()}
<hr />
{this.changePasswordModal()}
<Button className="w-100 m-t-10" onClick={this.openChangePasswordModal}>Change Password</Button>
{currentUser.isAdmin && <Button className="w-100 m-t-10">Send Password Reset Email</Button>}
{currentUser.isAdmin && this.renderPasswordReset()}
</React.Fragment>
)}
</div>
Expand Down
16 changes: 0 additions & 16 deletions client/app/pages/users/show.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,15 @@
<user-edit ng-if="userInfo && (currentUser.isAdmin || currentUser.id == user.id)" user="userInfo"></user-edit>

<div class="col-md-4 col-md-offset-4 profile__container" ng-if="(currentUser.isAdmin || currentUser.id == user.id)">
<hr>

<div ng-if="user.is_disabled" class="d-flex justify-content-between align-items-center">
<strong>This user is disabled.</strong>
<button type="button" class="btn btn-primary" ng-click="enableUser(user)">Enable</button>
</div>

<div ng-if="currentUser.isAdmin">
<div class="form-group">
<button class="btn btn-default" ng-if="!user.is_invitation_pending" ng-click="sendPasswordReset()" ng-disabled="disablePasswordResetButton">Send
Password Reset Email
</button>
<button class="btn btn-default" ng-if="user.is_invitation_pending" ng-click="resendInvitation()" ng-disabled="disablePasswordResetButton">Resend Invitation</button>
</div>

<div ng-if="passwordResetLink" class="alert alert-success">
<p ng-if="!clientConfig.mailSettingMissing">
<strong>The user should receive a link to reset their password by email soon.</strong>
</p>
<p ng-if="clientConfig.mailSettingsMissing">
You don't have mail server configured, please send the following link
to {{user.name}} to reset their password:<br/>
<textarea class="form-control m-t-10" rows="2" readonly>{{ passwordResetLink }}</textarea>
</p>
</div>
</div>
</div>
</div>
Expand Down
12 changes: 1 addition & 11 deletions client/app/pages/users/show.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { each } from 'lodash';
import settingsMenu from '@/services/settingsMenu';
import { absoluteUrl } from '@/services/utils';
import template from './show.html';
import './settings.less';

function UserCtrl(
$scope, $routeParams, $http, $location, toastr,
clientConfig, currentUser, User,
currentUser, User,
) {
$scope.userId = $routeParams.userId;
$scope.currentUser = currentUser;
$scope.clientConfig = clientConfig;

if ($scope.userId === undefined) {
$scope.userId = currentUser.id;
Expand Down Expand Up @@ -57,14 +55,6 @@ function UserCtrl(
};
});

$scope.sendPasswordReset = () => {
$scope.disablePasswordResetButton = true;
$http.post(`api/users/${$scope.user.id}/reset_password`).success((data) => {
$scope.disablePasswordResetButton = false;
$scope.passwordResetLink = absoluteUrl(data.reset_link);
});
};

$scope.resendInvitation = () => {
$http.post(`api/users/${$scope.user.id}/invite`).success(() => {
toastr.success('Invitation sent.', {
Expand Down
23 changes: 19 additions & 4 deletions client/app/services/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,29 @@ function deleteUser(user) {
function regenerateApiKey(user) {
return $http
.post(`api/users/${user.id}/regenerate_api_key`)
.success((data) => {
.then(({ data }) => {
toastr.success('The API Key has been updated.');
return data;
return data.api_key;
})
.error((response) => {
.catch((response) => {
const message =
response.data && response.data.message
? response.data.message
: `Failed regenerating API Key: ${response.statusText}`;

toastr.error(message);
});
}

function sendPasswordReset(user) {
return $http
.post(`api/users/${user.id}/reset_password`)
.then(({ data }) => data.reset_link)
.catch((response) => {
const message =
response.message
? response.message
: `Failed regenerating API Key: ${response.statusText}`;
: `Failed to send password reset email: ${response.statusText}`;

toastr.error(message);
});
Expand All @@ -98,6 +112,7 @@ function UserService($resource) {
UserResource.disableUser = disableUser;
UserResource.deleteUser = deleteUser;
UserResource.regenerateApiKey = regenerateApiKey;
UserResource.sendPasswordReset = sendPasswordReset;

return UserResource;
}
Expand Down

0 comments on commit d891df1

Please sign in to comment.