Skip to content

Commit b4d35ea

Browse files
getsentry-botdashed
andcommitted
Revert "feat(billing): update cancellation reasons (#90593)"
This reverts commit 7711e7a. Co-authored-by: dashed <[email protected]>
1 parent 8f875d9 commit b4d35ea

File tree

2 files changed

+45
-160
lines changed

2 files changed

+45
-160
lines changed

static/gsApp/views/cancelSubscription.spec.tsx

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ describe('CancelSubscription', function () {
4545
await screen.findByRole('heading', {name: 'Cancel Subscription'})
4646
).toBeInTheDocument();
4747
expect(
48-
await screen.findByText('The project/product/company is shutting down.')
48+
await screen.findByRole('radio', {
49+
name: 'The project/product/company is shutting down.',
50+
})
4951
).toBeInTheDocument();
5052
});
5153

@@ -65,9 +67,9 @@ describe('CancelSubscription', function () {
6567
it('displays followup textarea when option is selected', async function () {
6668
render(<CancelSubscription />);
6769

68-
const radio = await screen.findByText(
69-
'The project/product/company is shutting down.'
70-
);
70+
const radio = await screen.findByRole('radio', {
71+
name: 'The project/product/company is shutting down.',
72+
});
7173
expect(radio).toBeInTheDocument();
7274

7375
expect(screen.queryByRole('textbox', {name: 'followup'})).not.toBeInTheDocument();
@@ -81,46 +83,19 @@ describe('CancelSubscription', function () {
8183
method: 'DELETE',
8284
});
8385
render(<CancelSubscription />);
84-
const radio = await screen.findByText('We are switching to a different solution.');
85-
expect(radio).toBeInTheDocument();
86-
87-
await userEvent.click(radio);
88-
await userEvent.type(screen.getByRole('textbox'), 'Cancellation reason');
89-
await userEvent.click(screen.getByRole('button', {name: /Cancel Subscription/}));
90-
91-
expect(mock).toHaveBeenCalledWith(
92-
`/customers/${organization.slug}/`,
93-
expect.objectContaining({
94-
data: {
95-
reason: 'competitor',
96-
followup: 'Cancellation reason',
97-
checkboxes: [],
98-
},
99-
})
100-
);
101-
});
102-
103-
it('calls cancel API with checkboxes', async function () {
104-
const mock = MockApiClient.addMockResponse({
105-
url: `/customers/${organization.slug}/`,
106-
method: 'DELETE',
107-
});
108-
render(<CancelSubscription />);
109-
const radio = await screen.findByText("Sentry doesn't fit our needs.");
86+
const radio = await screen.findByRole('radio', {name: 'Other'});
11087
expect(radio).toBeInTheDocument();
11188

11289
await userEvent.click(radio);
113-
await userEvent.click(screen.getByTestId('checkbox-reach_out'));
11490
await userEvent.type(screen.getByRole('textbox'), 'Cancellation reason');
11591
await userEvent.click(screen.getByRole('button', {name: /Cancel Subscription/}));
11692

11793
expect(mock).toHaveBeenCalledWith(
11894
`/customers/${organization.slug}/`,
11995
expect.objectContaining({
12096
data: {
121-
reason: 'not_a_fit',
97+
reason: 'other',
12298
followup: 'Cancellation reason',
123-
checkboxes: ['reach_out'],
12499
},
125100
})
126101
);

static/gsApp/views/cancelSubscription.tsx

Lines changed: 37 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import {Fragment, useCallback, useEffect, useState} from 'react';
22
import styled from '@emotion/styled';
33
import moment from 'moment-timezone';
44

5-
import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
5+
import {addSuccessMessage} from 'sentry/actionCreators/indicator';
66
import {Alert} from 'sentry/components/core/alert';
77
import {Button} from 'sentry/components/core/button';
8-
import {Checkbox} from 'sentry/components/core/checkbox';
98
import RadioGroupField from 'sentry/components/forms/fields/radioField';
109
import TextareaField from 'sentry/components/forms/fields/textareaField';
1110
import Form from 'sentry/components/forms/form';
@@ -35,64 +34,46 @@ import usePromotionTriggerCheck from 'getsentry/utils/usePromotionTriggerCheck';
3534
import withPromotions from 'getsentry/utils/withPromotions';
3635

3736
type CancelReason = [string, React.ReactNode];
38-
type CancelCheckbox = [string, React.ReactNode];
3937

40-
const CANCEL_STEPS: Array<{
41-
followup: React.ReactNode;
42-
reason: CancelReason;
43-
checkboxes?: CancelCheckbox[];
44-
}> = [
38+
const CANCEL_STEPS: Array<{followup: React.ReactNode; reason: CancelReason}> = [
4539
{
46-
reason: ['migration', t('Consolidating Sentry accounts.')],
47-
followup: t(
48-
'If migrating to another existing account, can you provide the org slug?'
49-
),
40+
reason: ['shutting_down', t('The project/product/company is shutting down.')],
41+
followup: t('Sorry to hear that! Anything more we should know?'),
5042
},
5143
{
52-
reason: ['competitor', t('We are switching to a different solution.')],
53-
followup: t("Care to share the solution you've chosen and why?"),
44+
reason: ['only_need_free', t('We only need the features on the free plan.')],
45+
followup: t(
46+
'Fair enough. Which features on the free plan are most important to you?'
47+
),
5448
},
5549
{
5650
reason: ['not_a_fit', t("Sentry doesn't fit our needs.")],
57-
followup: t('Give us more feedback?'),
58-
checkboxes: [
59-
[
60-
'reach_out',
61-
t(
62-
"Prefer to share feedback live? Let us know what you'd like to discuss and we'll have a Product Manager reach out!"
63-
),
64-
],
65-
],
51+
followup: t('Bummer. What features were missing for you?'),
6652
},
6753
{
68-
reason: ['pricing_expensive', t('Pricing is too expensive.')],
69-
followup: t('Anything more we should know?'),
70-
},
71-
{
72-
reason: ['pricing_value', t("I didn't get the value I wanted.")],
73-
followup: t('What was missing?'),
54+
reason: ['competitor', t('We are switching to a different solution.')],
55+
followup: t('Thanks for letting us know. Which solution(s)? Why?'),
7456
},
7557
{
76-
reason: ['only_need_free', t('We only need the free plan.')],
77-
followup: t('Fair enough. Anything more we should know?'),
78-
checkboxes: [
79-
['features', t("I don't need so much volume.")],
80-
['volume', t('Developer features are enough for me.')],
81-
],
58+
reason: ['pricing', t("The pricing doesn't fit our needs.")],
59+
followup: t("What about it wasn't right for you?"),
8260
},
8361
{
8462
reason: ['self_hosted', t('We are hosting Sentry ourselves.')],
8563
followup: t('Are you interested in a single tenant version of Sentry?'),
8664
},
8765
{
88-
reason: ['shutting_down', t('The project/product/company is shutting down.')],
89-
followup: t('Sorry to hear that! Anything more we should know?'),
66+
reason: ['no_more_errors', t('We no longer get any errors.')],
67+
followup: t("Congrats! What's your secret?"),
68+
},
69+
{
70+
reason: ['other', t('Other')],
71+
followup: t('Other reason?'),
9072
},
9173
];
9274

9375
type State = {
9476
canSubmit: boolean;
95-
checkboxes: Record<string, boolean>;
9677
showFollowup: boolean;
9778
understandsMembers: boolean;
9879
val: CancelReason[0] | null;
@@ -101,7 +82,6 @@ type State = {
10182
function CancelSubscriptionForm() {
10283
const organization = useOrganization();
10384
const navigate = useNavigate();
104-
const api = useApi();
10585
const {data: subscription, isPending} = useApiQuery<Subscription>(
10686
[`/customers/${organization.slug}/`],
10787
{staleTime: 0}
@@ -111,37 +91,8 @@ function CancelSubscriptionForm() {
11191
showFollowup: false,
11292
understandsMembers: false,
11393
val: null,
114-
checkboxes: {},
11594
});
11695

117-
const handleSubmitSuccess = (resp: any) => {
118-
subscriptionStore.loadData(organization.slug);
119-
const msg = resp?.responseJSON?.details || t('Successfully cancelled subscription');
120-
121-
addSuccessMessage(msg);
122-
navigate({
123-
pathname: normalizeUrl(`/settings/${organization.slug}/billing/`),
124-
});
125-
};
126-
127-
const handleSubmit = async (data: any) => {
128-
try {
129-
const submitData = {
130-
...data,
131-
checkboxes: Object.keys(state.checkboxes).filter(key => state.checkboxes[key]),
132-
};
133-
134-
const response = await api.requestPromise(`/customers/${subscription?.slug}/`, {
135-
method: 'DELETE',
136-
data: submitData,
137-
});
138-
139-
handleSubmitSuccess(response);
140-
} catch (error) {
141-
addErrorMessage(error.responseJSON?.detail || t('Failed to cancel subscription'));
142-
}
143-
};
144-
14596
if (isPending || !subscription) {
14697
return <LoadingIndicator />;
14798
}
@@ -188,6 +139,16 @@ function CancelSubscriptionForm() {
188139

189140
const followup = CANCEL_STEPS.find(cancel => cancel.reason[0] === state.val)?.followup;
190141

142+
const handleSubmitSuccess = (resp: any) => {
143+
subscriptionStore.loadData(organization.slug);
144+
const msg = resp?.responseJSON?.details || t('Successfully cancelled subscription');
145+
146+
addSuccessMessage(msg);
147+
navigate({
148+
pathname: normalizeUrl(`/settings/${organization.slug}/billing/`),
149+
});
150+
};
151+
191152
return (
192153
<Fragment>
193154
<Alert.Container>
@@ -214,51 +175,27 @@ function CancelSubscriptionForm() {
214175
<PanelHeader>{t('Cancellation Reason')}</PanelHeader>
215176

216177
<PanelBody withPadding>
217-
<Form onSubmit={handleSubmit} onSubmitSuccess={handleSubmitSuccess} hideFooter>
178+
<Form
179+
apiMethod="DELETE"
180+
apiEndpoint={`/customers/${subscription.slug}/`}
181+
onSubmitSuccess={handleSubmitSuccess}
182+
hideFooter
183+
>
218184
<TextBlock>
219185
{t('Please help us understand why you are cancelling:')}
220186
</TextBlock>
221187

222-
<RadioGroupContainer
188+
<RadioGroupField
223189
stacked
224190
name="reason"
225191
label=""
226192
inline={false}
227-
choices={CANCEL_STEPS.map<CancelReason>(cancel => [
228-
cancel.reason[0],
229-
<RadioContainer key={cancel.reason[0]}>
230-
{cancel.reason[1]}
231-
{cancel.checkboxes && state.val === cancel.reason[0] && (
232-
<Fragment>
233-
{cancel.checkboxes.map(([name, label]) => (
234-
<ExtraContainer key={name}>
235-
<Checkbox
236-
data-test-id={`checkbox-${name}`}
237-
checked={state.checkboxes[name]}
238-
name={name}
239-
onChange={(value: any) => {
240-
setState(currentState => ({
241-
...currentState,
242-
checkboxes: {
243-
...currentState.checkboxes,
244-
[name]: value.target.checked,
245-
},
246-
}));
247-
}}
248-
/>
249-
{label}
250-
</ExtraContainer>
251-
))}
252-
</Fragment>
253-
)}
254-
</RadioContainer>,
255-
])}
193+
choices={CANCEL_STEPS.map<CancelReason>(cancel => cancel.reason)}
256194
onChange={(val: any) =>
257195
setState(currentState => ({
258196
...currentState,
259197
canSubmit: true,
260198
showFollowup: true,
261-
checkboxes: {},
262199
val,
263200
}))
264201
}
@@ -339,33 +276,6 @@ function CancelSubscriptionWrapper({
339276
);
340277
}
341278

342-
const RadioContainer = styled('div')`
343-
display: flex;
344-
flex-direction: column;
345-
346-
label {
347-
grid-template-columns: max-content 1fr;
348-
grid-template-rows: auto auto;
349-
350-
> div:last-child {
351-
grid-column: 2;
352-
}
353-
}
354-
`;
355-
356-
const RadioGroupContainer = styled(RadioGroupField)`
357-
label {
358-
align-items: flex-start;
359-
}
360-
`;
361-
362-
const ExtraContainer = styled('div')`
363-
display: flex;
364-
align-items: center;
365-
gap: ${space(1)};
366-
padding: ${space(1)} 0;
367-
`;
368-
369279
export default withSubscription(withPromotions(CancelSubscriptionWrapper), {
370280
noLoader: true,
371281
});

0 commit comments

Comments
 (0)