Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ref(js): Convert OrganizationDetails* to functional components #28921

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions static/app/views/organizationDetails/body.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {Fragment, useState} from 'react';

import AlertActions from 'app/actions/alertActions';
import Alert from 'app/components/alert';
import Button from 'app/components/button';
import ErrorBoundary from 'app/components/errorBoundary';
import Footer from 'app/components/footer';
import {Body, Main} from 'app/components/layouts/thirds';
import {IconWarning} from 'app/icons';
import {t, tct} from 'app/locale';
import {Organization} from 'app/types';
import useApi from 'app/utils/useApi';
import withOrganization from 'app/utils/withOrganization';

type Props = {
organization: Organization;
children?: React.ReactNode;
};

function DeletionInProgress({organization}: Props) {
return (
<Body>
<Main>
<Alert type="warning" icon={<IconWarning />}>
{tct(
'The [organization] organization is currently in the process of being deleted from Sentry.',
{
organization: <strong>{organization.slug}</strong>,
}
)}
</Alert>
</Main>
</Body>
);
}

function DeletionPending({organization}: Props) {
const api = useApi();
const [isRestoring, setIsRestoring] = useState(false);

const onRestore = async () => {
setIsRestoring(true);

try {
await api.requestPromise(`/organizations/${organization.slug}/`, {
method: 'PUT',
data: {cancelDeletion: true},
});
window.location.reload();
} catch {
setIsRestoring(false);
AlertActions.addAlert({
message:
'We were unable to restore this organization. Please try again or contact support.',
type: 'error',
});
}
};

return (
<Body>
<Main>
<h3>{t('Deletion Scheduled')}</h3>
<p>
{tct('The [organization] organization is currently scheduled for deletion.', {
organization: <strong>{organization.slug}</strong>,
})}
</p>

{organization.access.includes('org:admin') ? (
<div>
<p>
{t(
'Would you like to cancel this process and restore the organization back to the original state?'
)}
</p>
<p>
<Button priority="primary" onClick={onRestore} disabled={isRestoring}>
{t('Restore Organization')}
</Button>
</p>
</div>
) : (
<p>
{t(
'If this is a mistake, contact an organization owner and ask them to restore this organization.'
)}
</p>
)}
<p>
<small>
{t(
"Note: Restoration is available until the process begins. Once it does, there's no recovering the data that has been removed."
)}
</small>
</p>
</Main>
</Body>
);
}

function OrganizationDetailsBody({children, organization}: Props) {
const status = organization?.status?.id;

if (status === 'pending_deletion') {
return <DeletionPending organization={organization} />;
}

if (status === 'deletion_in_progress') {
return <DeletionInProgress organization={organization} />;
}

return (
<Fragment>
<ErrorBoundary>{children}</ErrorBoundary>
<Footer />
</Fragment>
);
}

export default withOrganization(OrganizationDetailsBody);
178 changes: 12 additions & 166 deletions static/app/views/organizationDetails/index.tsx
Original file line number Diff line number Diff line change
@@ -1,177 +1,23 @@
import {Component, Fragment} from 'react';
import {useEffect} from 'react';
import {RouteComponentProps} from 'react-router';

import {switchOrganization} from 'app/actionCreators/organizations';
import AlertActions from 'app/actions/alertActions';
import {Client} from 'app/api';
import Alert from 'app/components/alert';
import Button from 'app/components/button';
import ErrorBoundary from 'app/components/errorBoundary';
import Footer from 'app/components/footer';
import {Body, Main} from 'app/components/layouts/thirds';
import {IconWarning} from 'app/icons';
import {t, tct} from 'app/locale';
import {Organization} from 'app/types';
import withOrganization from 'app/utils/withOrganization';
import OrganizationContextContainer from 'app/views/organizationContext';

type InProgressProps = {
organization: Organization;
};
import Body from './body';

function DeletionInProgress({organization}: InProgressProps) {
return (
<Body>
<Main>
<Alert type="warning" icon={<IconWarning />}>
{tct(
'The [organization] organization is currently in the process of being deleted from Sentry.',
{
organization: <strong>{organization.slug}</strong>,
}
)}
</Alert>
</Main>
</Body>
);
}

type PendingProps = {
organization: Organization;
};

type PendingState = {
submitInProgress: boolean;
};
type Props = RouteComponentProps<{orgId: string}, {}> &
Partial<React.ComponentProps<typeof OrganizationContextContainer>>;

class DeletionPending extends Component<PendingProps, PendingState> {
state: PendingState = {submitInProgress: false};

componentWillUnmount() {
this.api.clear();
}

api = new Client();

onRestore = () => {
if (this.state.submitInProgress) {
return;
}
this.setState({submitInProgress: true});
this.api.request(`/organizations/${this.props.organization.slug}/`, {
method: 'PUT',
data: {cancelDeletion: true},
success: () => {
window.location.reload();
},
error: () => {
AlertActions.addAlert({
message:
'We were unable to restore this organization. Please try again or contact support.',
type: 'error',
});
this.setState({submitInProgress: false});
},
});
};

render() {
const {organization} = this.props;
const access = new Set(organization.access);
return (
<Body>
<Main>
<h3>{t('Deletion Scheduled')}</h3>
<p>
{tct('The [organization] organization is currently scheduled for deletion.', {
organization: <strong>{organization.slug}</strong>,
})}
</p>

{access.has('org:admin') ? (
<div>
<p>
{t(
'Would you like to cancel this process and restore the organization back to the original state?'
)}
</p>
<p>
<Button
priority="primary"
onClick={this.onRestore}
disabled={this.state.submitInProgress}
>
{t('Restore Organization')}
</Button>
</p>
</div>
) : (
<p>
{t(
'If this is a mistake, contact an organization owner and ask them to restore this organization.'
)}
</p>
)}
<p>
<small>
{t(
"Note: Restoration is available until the process begins. Once it does, there's no recovering the data that has been removed."
)}
</small>
</p>
</Main>
</Body>
);
}
}

type OrganizationDetailsProps = {
organization?: Organization;
children?: React.ReactNode;
};

const OrganizationDetailsBody = withOrganization(function OrganizationDetailsBody({
children,
organization,
}: OrganizationDetailsProps) {
const status = organization?.status?.id;

if (organization && status === 'pending_deletion') {
return <DeletionPending organization={organization} />;
}

if (organization && status === 'deletion_in_progress') {
return <DeletionInProgress organization={organization} />;
}
function OrganizationDetails({children, ...props}: Props) {
// Switch organizations when the orgId changes
useEffect(() => void switchOrganization(), [props.params.orgId]);

return (
<Fragment>
<ErrorBoundary>{children}</ErrorBoundary>
<Footer />
</Fragment>
<OrganizationContextContainer includeSidebar useLastOrganization {...props}>
<Body>{children}</Body>
</OrganizationContextContainer>
);
});

type Props = RouteComponentProps<{orgId: string}, {}>;

export default class OrganizationDetails extends Component<Props> {
componentDidUpdate(prevProps: Props) {
if (
prevProps.params &&
this.props.params &&
prevProps.params.orgId !== this.props.params.orgId
) {
switchOrganization();
}
}

render() {
return (
<OrganizationContextContainer includeSidebar useLastOrganization {...this.props}>
<OrganizationDetailsBody {...this.props}>
{this.props.children}
</OrganizationDetailsBody>
</OrganizationContextContainer>
);
}
}

export default OrganizationDetails;