import styled from '@emotion/styled'; import {addErrorMessage, addLoadingMessage} from 'sentry/actionCreators/indicator'; import {ModalRenderProps, openModal} from 'sentry/actionCreators/modal'; import {Alert} from 'sentry/components/alert'; import {Button} from 'sentry/components/button'; import HookOrDefault from 'sentry/components/hookOrDefault'; import Panel from 'sentry/components/panels/panel'; import PanelAlert from 'sentry/components/panels/panelAlert'; import PanelBody from 'sentry/components/panels/panelBody'; import PanelHeader from 'sentry/components/panels/panelHeader'; import PanelItem from 'sentry/components/panels/panelItem'; import {t, tct} from 'sentry/locale'; import {Organization} from 'sentry/types'; import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView'; import {ConfirmAccountClose} from 'sentry/views/settings/account/confirmAccountClose'; import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader'; import TextBlock from 'sentry/views/settings/components/text/textBlock'; const BYE_URL = '/'; const leaveRedirect = () => (window.location.href = BYE_URL); const Important = styled('div')` font-weight: bold; font-size: 1.2em; `; function GoodbyeModalContent({Header, Body, Footer}: ModalRenderProps) { return (
{t('Closing Account')}
{t('Your account has been deactivated and scheduled for removal.')} {t('Thanks for using Sentry! We hope to see you again soon!')}
); } type OwnedOrg = { organization: Organization; singleOwner: boolean; }; type Props = DeprecatedAsyncView['props']; type State = DeprecatedAsyncView['state'] & { organizations: OwnedOrg[] | null; /** * Org slugs that will be removed */ orgsToRemove: Set | null; }; class AccountClose extends DeprecatedAsyncView { leaveRedirectTimeout: number | undefined = undefined; componentWillUnmount() { window.clearTimeout(this.leaveRedirectTimeout); } getEndpoints(): ReturnType { return [['organizations', '/organizations/?owner=1']]; } getDefaultState() { return { ...super.getDefaultState(), orgsToRemove: null, }; } get singleOwnerOrgs() { return this.state.organizations ?.filter(({singleOwner}) => singleOwner) ?.map(({organization}) => organization.slug); } getTitle() { return t('Close Account'); } handleChange = ( {slug}: Organization, isSingle: boolean, event: React.ChangeEvent ) => { const checked = event.target.checked; // Can't unselect an org where you are the single owner if (isSingle) { return; } this.setState(state => { const set = state.orgsToRemove || new Set(this.singleOwnerOrgs); if (checked) { set.add(slug); } else { set.delete(slug); } return {orgsToRemove: set}; }); }; get orgSlugsToRemove() { const {orgsToRemove} = this.state; return ( (orgsToRemove === null ? this.singleOwnerOrgs : Array.from(orgsToRemove)) || [] ); } handleRemoveAccount = async () => { const orgs = this.orgSlugsToRemove; addLoadingMessage('Closing account\u2026'); try { await this.api.requestPromise('/users/me/', { method: 'DELETE', data: {organizations: orgs}, }); openModal(GoodbyeModalContent, { onClose: leaveRedirect, }); // Redirect after 10 seconds window.clearTimeout(this.leaveRedirectTimeout); this.leaveRedirectTimeout = window.setTimeout(leaveRedirect, 10000); } catch { addErrorMessage('Error closing account'); } }; renderBody() { const {organizations, orgsToRemove} = this.state; const HookedCustomConfirmAccountClose = HookOrDefault({ hookName: 'component:confirm-account-close', defaultComponent: props => , }); return (
{t('This will permanently remove all associated data for your user')}. {t('Closing your account is permanent and cannot be undone')}! {t('Remove the following organizations')} {t( 'Ownership will remain with other organization owners if an organization is not deleted.' )}
{tct( "Boxes which can't be unchecked mean that you are the only organization owner and the organization [strong:will be deleted].", {strong: } )}
{organizations?.map(({organization, singleOwner}) => ( ))}
); } } export default AccountClose;