123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- import {Component, Fragment} from 'react';
- import type {RouteComponentProps} 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/user';
- import {browserHistory} from 'sentry/utils/browserHistory';
- 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;
|