Files
2024-02-15 17:01:50 +00:00

222 lines
5.8 KiB
TypeScript

import {Component, Fragment} from 'react';
import type {RouteComponentProps} from 'react-router';
import {browserHistory} from 'react-router';
import styled from '@emotion/styled';
import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
import type {ModalRenderProps} from 'sentry/actionCreators/modal';
import {openModal} from 'sentry/actionCreators/modal';
import {Button} from 'sentry/components/button';
import RadioGroup from 'sentry/components/forms/controls/radioGroup';
import Form from 'sentry/components/forms/form';
import JsonForm from 'sentry/components/forms/jsonForm';
import FormModel from 'sentry/components/forms/model';
import type {JsonFormObject} from 'sentry/components/forms/types';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {User} from 'sentry/types';
import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView';
const userEditForm: JsonFormObject = {
title: 'User details',
fields: [
{
name: 'name',
type: 'string',
required: true,
label: t('Name'),
},
{
name: 'username',
type: 'string',
required: true,
label: t('Username'),
help: t('The username is the unique id of the user in the system'),
},
{
name: 'email',
type: 'string',
required: true,
label: t('Email'),
help: t('The users primary email address'),
},
{
name: 'isActive',
type: 'boolean',
required: true,
label: t('Active'),
help: t(
'Designates whether this user should be treated as active. Unselect this instead of deleting accounts.'
),
},
{
name: 'isStaff',
type: 'boolean',
required: true,
label: t('Admin'),
help: t('Designates whether this user can perform administrative functions.'),
},
{
name: 'isSuperuser',
type: 'boolean',
required: true,
label: t('Superuser'),
help: t(
'Designates whether this user has all permissions without explicitly assigning them.'
),
},
],
};
const REMOVE_BUTTON_LABEL = {
disable: t('Disable User'),
delete: t('Permanently Delete User'),
};
type DeleteType = 'disable' | 'delete';
type RemoveModalProps = ModalRenderProps & {
onRemove: (type: DeleteType) => void;
user: User;
};
type RemoveModalState = {
deleteType: DeleteType;
};
class RemoveUserModal extends Component<RemoveModalProps, RemoveModalState> {
state: RemoveModalState = {
deleteType: 'disable',
};
onRemove = () => {
this.props.onRemove(this.state.deleteType);
this.props.closeModal();
};
render() {
const {user} = this.props;
const {deleteType} = this.state;
return (
<Fragment>
<RadioGroup
value={deleteType}
label={t('Remove user %s', user.email)}
onChange={type => this.setState({deleteType: type})}
choices={[
['disable', t('Disable the account.')],
['delete', t('Permanently remove the user and their data.')],
]}
/>
<ModalFooter>
<Button priority="danger" onClick={this.onRemove}>
{REMOVE_BUTTON_LABEL[deleteType]}
</Button>
<Button onClick={this.props.closeModal}>{t('Cancel')}</Button>
</ModalFooter>
</Fragment>
);
}
}
type Props = DeprecatedAsyncView['props'] & RouteComponentProps<{id: string}, {}>;
type State = DeprecatedAsyncView['state'] & {
user: User | null;
};
class AdminUserEdit extends DeprecatedAsyncView<Props, State> {
get userEndpoint() {
const {params} = this.props;
return `/users/${params.id}/`;
}
getEndpoints(): ReturnType<DeprecatedAsyncView['getEndpoints']> {
return [['user', this.userEndpoint]];
}
async deleteUser() {
await this.api.requestPromise(this.userEndpoint, {
method: 'DELETE',
data: {hardDelete: true, organizations: []},
});
addSuccessMessage(t("%s's account has been deleted.", this.state.user?.email));
browserHistory.replace('/manage/users/');
}
async deactivateUser() {
const response = await this.api.requestPromise(this.userEndpoint, {
method: 'PUT',
data: {isActive: false},
});
this.setState({user: response});
this.formModel.setInitialData(response);
addSuccessMessage(t("%s's account has been deactivated.", response.email));
}
removeUser = (actionTypes: DeleteType) =>
actionTypes === 'delete' ? this.deleteUser() : this.deactivateUser();
formModel = new FormModel();
renderBody() {
const {user} = this.state;
if (user === null) {
return null;
}
const openDeleteModal = () =>
openModal(opts => (
<RemoveUserModal user={user} onRemove={this.removeUser} {...opts} />
));
return (
<Fragment>
<h3>{t('Users')}</h3>
<p>{t('Editing user: %s', user.email)}</p>
<Form
model={this.formModel}
initialData={user}
apiMethod="PUT"
apiEndpoint={this.userEndpoint}
requireChanges
onSubmitError={err => {
addErrorMessage(err?.responseJSON?.detail);
}}
onSubmitSuccess={data => {
this.setState({user: data});
addSuccessMessage(t('User account updated.'));
}}
extraButton={
<Button
onClick={openDeleteModal}
style={{marginLeft: space(1)}}
priority="danger"
>
{t('Remove User')}
</Button>
}
>
<JsonForm forms={[userEditForm]} />
</Form>
</Fragment>
);
}
}
const ModalFooter = styled('div')`
display: grid;
grid-auto-flow: column;
gap: ${space(1)};
justify-content: end;
padding: 20px 30px;
margin: 20px -30px -30px;
border-top: 1px solid ${p => p.theme.border};
`;
export default AdminUserEdit;