index.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import {Fragment, useMemo} from 'react';
  2. import cloneDeep from 'lodash/cloneDeep';
  3. import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
  4. import {removeTeam, updateTeamSuccess} from 'sentry/actionCreators/teams';
  5. import {hasEveryAccess} from 'sentry/components/acl/access';
  6. import Alert from 'sentry/components/alert';
  7. import {Button} from 'sentry/components/button';
  8. import Confirm from 'sentry/components/confirm';
  9. import FieldGroup from 'sentry/components/forms/fieldGroup';
  10. import type {FormProps} from 'sentry/components/forms/form';
  11. import Form from 'sentry/components/forms/form';
  12. import JsonForm from 'sentry/components/forms/jsonForm';
  13. import type {FieldObject} from 'sentry/components/forms/types';
  14. import Panel from 'sentry/components/panels/panel';
  15. import PanelHeader from 'sentry/components/panels/panelHeader';
  16. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  17. import teamSettingsFields from 'sentry/data/forms/teamSettingsFields';
  18. import {IconDelete} from 'sentry/icons';
  19. import {t, tct} from 'sentry/locale';
  20. import type {RouteComponentProps} from 'sentry/types/legacyReactRouter';
  21. import type {Team} from 'sentry/types/organization';
  22. import {browserHistory} from 'sentry/utils/browserHistory';
  23. import normalizeUrl from 'sentry/utils/url/normalizeUrl';
  24. import useApi from 'sentry/utils/useApi';
  25. import useOrganization from 'sentry/utils/useOrganization';
  26. import PermissionAlert from 'sentry/views/settings/project/permissionAlert';
  27. interface TeamSettingsProps extends RouteComponentProps<{teamId: string}, {}> {
  28. team: Team;
  29. }
  30. function TeamSettings({team, params}: TeamSettingsProps) {
  31. const organization = useOrganization();
  32. const api = useApi();
  33. const handleSubmitSuccess: FormProps['onSubmitSuccess'] = (resp: Team, _model, id) => {
  34. // Use the old slug when triggering the update so we correctly replace the
  35. // previous team in the store
  36. updateTeamSuccess(team.slug, resp);
  37. if (id === 'slug') {
  38. addSuccessMessage(t('Team name changed'));
  39. browserHistory.replace(
  40. normalizeUrl(`/settings/${organization.slug}/teams/${resp.slug}/settings/`)
  41. );
  42. }
  43. };
  44. const handleRemoveTeam = async () => {
  45. try {
  46. await removeTeam(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. const hasTeamWrite = hasEveryAccess(['team:write'], {organization, team});
  53. const hasTeamAdmin = hasEveryAccess(['team:admin'], {organization, team});
  54. const isIdpProvisioned = team.flags['idp:provisioned'];
  55. const forms = useMemo(() => {
  56. const formsConfig = cloneDeep(teamSettingsFields);
  57. const teamIdField: FieldObject = {
  58. name: 'teamId',
  59. type: 'string',
  60. disabled: true,
  61. label: t('Team ID'),
  62. setValue(_, _name) {
  63. return team.id;
  64. },
  65. help: `The unique identifier for this team. It cannot be modified.`,
  66. };
  67. formsConfig[0].fields = [...formsConfig[0].fields, teamIdField];
  68. return formsConfig;
  69. }, [team]);
  70. return (
  71. <Fragment>
  72. <SentryDocumentTitle title={t('Team Settings')} orgSlug={organization.slug} />
  73. <PermissionAlert access={['team:write']} team={team} />
  74. {isIdpProvisioned && (
  75. <Alert type="warning" showIcon>
  76. {t(
  77. "This team is managed through your organization's identity provider. These settings cannot be modified."
  78. )}
  79. </Alert>
  80. )}
  81. <Form
  82. apiMethod="PUT"
  83. apiEndpoint={`/teams/${organization.slug}/${team.slug}/`}
  84. saveOnBlur
  85. allowUndo
  86. onSubmitSuccess={handleSubmitSuccess}
  87. onSubmitError={() => addErrorMessage(t('Unable to save change'))}
  88. initialData={{
  89. name: team.name,
  90. slug: team.slug,
  91. }}
  92. >
  93. <JsonForm
  94. additionalFieldProps={{
  95. hasTeamWrite,
  96. }}
  97. disabled={isIdpProvisioned}
  98. forms={forms}
  99. />
  100. </Form>
  101. <Panel>
  102. <PanelHeader>{t('Team Administration')}</PanelHeader>
  103. <FieldGroup
  104. disabled={isIdpProvisioned}
  105. label={t('Remove Team')}
  106. help={t(
  107. "This may affect team members' access to projects and associated alert delivery."
  108. )}
  109. >
  110. <div>
  111. <Confirm
  112. disabled={isIdpProvisioned || !hasTeamAdmin}
  113. onConfirm={handleRemoveTeam}
  114. priority="danger"
  115. message={tct('Are you sure you want to remove the team [team]?', {
  116. team: `#${team.slug}`,
  117. })}
  118. >
  119. <Button
  120. icon={<IconDelete />}
  121. priority="danger"
  122. data-test-id="button-remove-team"
  123. >
  124. {t('Remove Team')}
  125. </Button>
  126. </Confirm>
  127. </div>
  128. </FieldGroup>
  129. </Panel>
  130. </Fragment>
  131. );
  132. }
  133. export default TeamSettings;