Files
redash/client/app/components/users/UserEdit.jsx
Gabriel Dutra 6a75ac4a57 Migrate User Pages to React (#3506)
* Create React version for the EmailSettingsWarning

* Migrate the Create User Page

* Migrate UserProfile to React

* Add /users/me to the routes (Percy ftw)

* Fix UserShow test spec

* Remove Error Messages component

* Show invitation link if email server not setup (#3519)

* return invite link to client if e-mail server is not set up

* add a couple of tests to make sure invite links are only returned when neccessary

* show invite link when e-mail is not configured

* remove "an e-mail has been sent" when there's no e-mail configured

* return invite_url in re-invites as well. Also refactor to reuse the code.

* Use CreateUserDialog instead of Page

* Render invite link on Resend Invitation click

* Add email validation to DynamicForm

* Fix EmailWarning position + update user list with user creation success

* Fix console error on UserProfile

* Redirect from /users/new  + rename createUser -> showCreateUserDialog

* Use alert instead of toastr for user creation errors

* Remove logic from CreateUserDialog

* CR

* Use Promise.reject instead of throw to avoid console error
2019-03-04 18:26:51 -03:00

256 lines
6.7 KiB
JavaScript

import React, { Fragment } from 'react';
import Alert from 'antd/lib/alert';
import Button from 'antd/lib/button';
import Form from 'antd/lib/form';
import Modal from 'antd/lib/modal';
import { User } from '@/services/user';
import { currentUser } from '@/services/auth';
import { absoluteUrl } from '@/services/utils';
import { UserProfile } from '../proptypes';
import { DynamicForm } from '../dynamic-form/DynamicForm';
import ChangePasswordDialog from './ChangePasswordDialog';
import InputWithCopy from '../InputWithCopy';
export default class UserEdit extends React.Component {
static propTypes = {
user: UserProfile.isRequired,
};
constructor(props) {
super(props);
this.state = {
user: this.props.user,
regeneratingApiKey: false,
sendingPasswordEmail: false,
resendingInvitation: false,
togglingUser: false,
};
}
changePassword = () => {
ChangePasswordDialog.showModal({ user: this.props.user });
};
sendPasswordReset = () => {
this.setState({ sendingPasswordEmail: true });
User.sendPasswordReset(this.state.user).then((passwordLink) => {
this.setState({ passwordLink });
}).finally(() => {
this.setState({ sendingPasswordEmail: false });
});
};
resendInvitation = () => {
this.setState({ resendingInvitation: true });
User.resendInvitation(this.state.user).then((passwordLink) => {
this.setState({ passwordLink });
}).finally(() => {
this.setState({ resendingInvitation: false });
});
};
regenerateApiKey = () => {
const doRegenerate = () => {
this.setState({ regeneratingApiKey: true });
User.regenerateApiKey(this.state.user).then((apiKey) => {
if (apiKey) {
const { user } = this.state;
this.setState({ user: { ...user, apiKey } });
}
}).finally(() => {
this.setState({ regeneratingApiKey: false });
});
};
Modal.confirm({
title: 'Regenerate API Key',
content: 'Are you sure you want to regenerate?',
okText: 'Regenerate',
onOk: doRegenerate,
maskClosable: true,
autoFocusButton: null,
});
};
toggleUser = () => {
const { user } = this.state;
const toggleUser = user.isDisabled ? User.enableUser : User.disableUser;
this.setState({ togglingUser: true });
toggleUser(user).then((data) => {
if (data) {
this.setState({ user: User.convertUserInfo(data.data) });
}
}).finally(() => {
this.setState({ togglingUser: false });
});
};
saveUser = (values, successCallback, errorCallback) => {
const data = {
id: this.props.user.id,
...values,
};
User.save(data, (user) => {
successCallback('Saved.');
this.setState({ user: User.convertUserInfo(user) });
}, (error = {}) => {
errorCallback(error.data && error.data.message || 'Failed saving.');
});
};
renderBasicInfoForm() {
const { user } = this.state;
const formFields = [
{
name: 'name',
title: 'Name',
type: 'text',
initialValue: user.name,
},
{
name: 'email',
title: 'Email',
type: 'email',
initialValue: user.email,
},
].map(field => ({ ...field, readOnly: user.isDisabled, required: true }));
return (
<DynamicForm
fields={formFields}
onSubmit={this.saveUser}
hideSubmitButton={user.isDisabled}
/>
);
}
renderApiKey() {
const { user, regeneratingApiKey } = this.state;
return (
<Form layout="vertical">
<hr />
<Form.Item label="API Key" className="m-b-10">
<InputWithCopy id="apiKey" value={user.apiKey} data-test="ApiKey" readOnly />
</Form.Item>
<Button
className="w-100"
onClick={this.regenerateApiKey}
loading={regeneratingApiKey}
data-test="RegenerateApiKey"
>
Regenerate
</Button>
</Form>
);
}
renderPasswordLinkAlert() {
const { user, passwordLink } = this.state;
return (
<Alert
message="Email not sent!"
description={(
<Fragment>
<p>
The mail server is not configured, please send the following link
to <b>{user.name}</b>:
</p>
<InputWithCopy value={absoluteUrl(passwordLink)} readOnly />
</Fragment>
)}
type="warning"
className="m-t-20"
afterClose={() => { this.setState({ passwordLink: null }); }}
closable
/>
);
}
renderResendInvitation() {
return (
<Button
className="w-100 m-t-10"
onClick={this.resendInvitation}
loading={this.state.resendingInvitation}
>
Resend Invitation
</Button>
);
}
renderSendPasswordReset() {
const { sendingPasswordEmail } = this.state;
return (
<Fragment>
<Button
className="w-100 m-t-10"
onClick={this.sendPasswordReset}
loading={sendingPasswordEmail}
>
Send Password Reset Email
</Button>
</Fragment>
);
}
rendertoggleUser() {
const { user, togglingUser } = this.state;
return user.isDisabled ? (
<Button className="w-100 m-t-10" type="primary" onClick={this.toggleUser} loading={togglingUser}>
Enable User
</Button>
) : (
<Button className="w-100 m-t-10" type="danger" onClick={this.toggleUser} loading={togglingUser}>
Disable User
</Button>
);
}
render() {
const { user, passwordLink } = this.state;
return (
<div className="col-md-4 col-md-offset-4">
<img
alt="Profile"
src={user.profileImageUrl}
className="profile__image"
width="40"
/>
<h3 className="profile__h3">{user.name}</h3>
<hr />
{this.renderBasicInfoForm()}
{!user.isDisabled && (
<Fragment>
{this.renderApiKey()}
<hr />
<h5>Password</h5>
{user.id === currentUser.id && (
<Button className="w-100 m-t-10" onClick={this.changePassword} data-test="ChangePassword">
Change Password
</Button>
)}
{(currentUser.isAdmin && user.id !== currentUser.id) && (
<Fragment>
{user.isInvitationPending ?
this.renderResendInvitation() : this.renderSendPasswordReset()}
{passwordLink && this.renderPasswordLinkAlert()}
</Fragment>
)}
</Fragment>
)}
<hr />
{currentUser.isAdmin && user.id !== currentUser.id && this.rendertoggleUser()}
</div>
);
}
}