@@ -2,10 +2,9 @@ import {Fragment, useCallback, useEffect, useState} from 'react';
2
2
import styled from '@emotion/styled' ;
3
3
import moment from 'moment-timezone' ;
4
4
5
- import { addErrorMessage , addSuccessMessage } from 'sentry/actionCreators/indicator' ;
5
+ import { addSuccessMessage } from 'sentry/actionCreators/indicator' ;
6
6
import { Alert } from 'sentry/components/core/alert' ;
7
7
import { Button } from 'sentry/components/core/button' ;
8
- import { Checkbox } from 'sentry/components/core/checkbox' ;
9
8
import RadioGroupField from 'sentry/components/forms/fields/radioField' ;
10
9
import TextareaField from 'sentry/components/forms/fields/textareaField' ;
11
10
import Form from 'sentry/components/forms/form' ;
@@ -35,64 +34,46 @@ import usePromotionTriggerCheck from 'getsentry/utils/usePromotionTriggerCheck';
35
34
import withPromotions from 'getsentry/utils/withPromotions' ;
36
35
37
36
type CancelReason = [ string , React . ReactNode ] ;
38
- type CancelCheckbox = [ string , React . ReactNode ] ;
39
37
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 } > = [
45
39
{
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?' ) ,
50
42
} ,
51
43
{
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
+ ) ,
54
48
} ,
55
49
{
56
50
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?' ) ,
66
52
} ,
67
53
{
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?' ) ,
74
56
} ,
75
57
{
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?" ) ,
82
60
} ,
83
61
{
84
62
reason : [ 'self_hosted' , t ( 'We are hosting Sentry ourselves.' ) ] ,
85
63
followup : t ( 'Are you interested in a single tenant version of Sentry?' ) ,
86
64
} ,
87
65
{
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?' ) ,
90
72
} ,
91
73
] ;
92
74
93
75
type State = {
94
76
canSubmit : boolean ;
95
- checkboxes : Record < string , boolean > ;
96
77
showFollowup : boolean ;
97
78
understandsMembers : boolean ;
98
79
val : CancelReason [ 0 ] | null ;
@@ -101,7 +82,6 @@ type State = {
101
82
function CancelSubscriptionForm ( ) {
102
83
const organization = useOrganization ( ) ;
103
84
const navigate = useNavigate ( ) ;
104
- const api = useApi ( ) ;
105
85
const { data : subscription , isPending} = useApiQuery < Subscription > (
106
86
[ `/customers/${ organization . slug } /` ] ,
107
87
{ staleTime : 0 }
@@ -111,37 +91,8 @@ function CancelSubscriptionForm() {
111
91
showFollowup : false ,
112
92
understandsMembers : false ,
113
93
val : null ,
114
- checkboxes : { } ,
115
94
} ) ;
116
95
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
-
145
96
if ( isPending || ! subscription ) {
146
97
return < LoadingIndicator /> ;
147
98
}
@@ -188,6 +139,16 @@ function CancelSubscriptionForm() {
188
139
189
140
const followup = CANCEL_STEPS . find ( cancel => cancel . reason [ 0 ] === state . val ) ?. followup ;
190
141
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
+
191
152
return (
192
153
< Fragment >
193
154
< Alert . Container >
@@ -214,51 +175,27 @@ function CancelSubscriptionForm() {
214
175
< PanelHeader > { t ( 'Cancellation Reason' ) } </ PanelHeader >
215
176
216
177
< 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
+ >
218
184
< TextBlock >
219
185
{ t ( 'Please help us understand why you are cancelling:' ) }
220
186
</ TextBlock >
221
187
222
- < RadioGroupContainer
188
+ < RadioGroupField
223
189
stacked
224
190
name = "reason"
225
191
label = ""
226
192
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 ) }
256
194
onChange = { ( val : any ) =>
257
195
setState ( currentState => ( {
258
196
...currentState ,
259
197
canSubmit : true ,
260
198
showFollowup : true ,
261
- checkboxes : { } ,
262
199
val,
263
200
} ) )
264
201
}
@@ -339,33 +276,6 @@ function CancelSubscriptionWrapper({
339
276
) ;
340
277
}
341
278
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
-
369
279
export default withSubscription ( withPromotions ( CancelSubscriptionWrapper ) , {
370
280
noLoader : true ,
371
281
} ) ;
0 commit comments