index.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import {Fragment} from 'react';
  2. import {browserHistory, RouteComponentProps} from 'react-router';
  3. import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
  4. import {removeTeam, updateTeamSuccess} from 'sentry/actionCreators/teams';
  5. import {Button} from 'sentry/components/button';
  6. import Confirm from 'sentry/components/confirm';
  7. import FieldGroup from 'sentry/components/forms/fieldGroup';
  8. import Form, {FormProps} from 'sentry/components/forms/form';
  9. import JsonForm from 'sentry/components/forms/jsonForm';
  10. import {Panel, PanelHeader} from 'sentry/components/panels';
  11. import teamSettingsFields from 'sentry/data/forms/teamSettingsFields';
  12. import {IconDelete} from 'sentry/icons';
  13. import {t, tct} from 'sentry/locale';
  14. import {Organization, Scope, Team} from 'sentry/types';
  15. import {normalizeUrl} from 'sentry/utils/withDomainRequired';
  16. import withOrganization from 'sentry/utils/withOrganization';
  17. import AsyncView from 'sentry/views/asyncView';
  18. type Props = RouteComponentProps<{teamId: string}, {}> & {
  19. organization: Organization;
  20. team: Team;
  21. };
  22. type State = AsyncView['state'];
  23. class TeamSettings extends AsyncView<Props, State> {
  24. getTitle() {
  25. return 'Team Settings';
  26. }
  27. getEndpoints() {
  28. return [];
  29. }
  30. handleSubmitSuccess: FormProps['onSubmitSuccess'] = (resp, _model, id) => {
  31. const {organization} = this.props;
  32. // Use the old slug when triggering the update so we correctly replace the
  33. // previous team in the store
  34. updateTeamSuccess(this.props.team.slug, resp);
  35. if (id === 'slug') {
  36. addSuccessMessage(t('Team name changed'));
  37. browserHistory.replace(
  38. normalizeUrl(`/settings/${organization.slug}/teams/${resp.slug}/settings/`)
  39. );
  40. this.setState({loading: true});
  41. }
  42. };
  43. handleRemoveTeam = async () => {
  44. const {organization, params} = this.props;
  45. try {
  46. await removeTeam(this.api, {orgId: organization.slug, teamId: params.teamId});
  47. browserHistory.replace(normalizeUrl(`/settings/${organization.slug}/teams/`));
  48. } catch {
  49. // removeTeam already displays an error message
  50. }
  51. };
  52. renderBody() {
  53. const {organization, team} = this.props;
  54. const access = new Set<Scope>(organization.access);
  55. return (
  56. <Fragment>
  57. <Form
  58. apiMethod="PUT"
  59. apiEndpoint={`/teams/${organization.slug}/${team.slug}/`}
  60. saveOnBlur
  61. allowUndo
  62. onSubmitSuccess={this.handleSubmitSuccess}
  63. onSubmitError={() => addErrorMessage(t('Unable to save change'))}
  64. initialData={{
  65. name: team.name,
  66. slug: team.slug,
  67. }}
  68. >
  69. <JsonForm access={access} forms={teamSettingsFields} />
  70. </Form>
  71. <Panel>
  72. <PanelHeader>{t('Remove Team')}</PanelHeader>
  73. <FieldGroup
  74. help={t(
  75. "This may affect team members' access to projects and associated alert delivery."
  76. )}
  77. >
  78. <div>
  79. <Confirm
  80. disabled={!access.has('team:admin')}
  81. onConfirm={this.handleRemoveTeam}
  82. priority="danger"
  83. message={tct('Are you sure you want to remove the team [team]?', {
  84. team: `#${team.slug}`,
  85. })}
  86. >
  87. <Button
  88. icon={<IconDelete />}
  89. priority="danger"
  90. disabled={!access.has('team:admin')}
  91. >
  92. {t('Remove Team')}
  93. </Button>
  94. </Confirm>
  95. </div>
  96. </FieldGroup>
  97. </Panel>
  98. </Fragment>
  99. );
  100. }
  101. }
  102. export default withOrganization(TeamSettings);