1
1
import { type PaymentInstrument } from '@bigcommerce/checkout-sdk' ;
2
2
import { noop } from 'lodash' ;
3
- import React , { Component , type ReactNode } from 'react' ;
3
+ import React , { type ReactElement , useState } from 'react' ;
4
4
5
5
import { TranslatedString } from '@bigcommerce/checkout/locale' ;
6
- import { CheckoutContext } from '@bigcommerce/checkout/payment-integration-api' ;
6
+ import { useCheckout } from '@bigcommerce/checkout/payment-integration-api' ;
7
7
import { Button , ButtonSize , ButtonVariant , Modal , ModalHeader } from '@bigcommerce/checkout/ui' ;
8
8
9
9
import {
@@ -26,71 +26,64 @@ export interface ManageInstrumentsModalProps {
26
26
onRequestClose ?( ) : void ;
27
27
}
28
28
29
- export interface ManageInstrumentsModalState {
30
- isConfirmingDelete : boolean ;
31
- selectedInstrumentId ?: string ;
32
- }
33
-
34
- class ManageInstrumentsModal extends Component <
35
- ManageInstrumentsModalProps ,
36
- ManageInstrumentsModalState
37
- > {
38
- static contextType = CheckoutContext ;
39
- declare context : React . ContextType < typeof CheckoutContext > ;
40
-
41
- state : ManageInstrumentsModalState = {
42
- isConfirmingDelete : false ,
29
+ const ManageInstrumentsModal = ( {
30
+ isOpen,
31
+ instruments,
32
+ onAfterOpen,
33
+ onDeleteInstrument = noop ,
34
+ onDeleteInstrumentError = noop ,
35
+ onRequestClose,
36
+ } : ManageInstrumentsModalProps ) : ReactElement => {
37
+ const [ isConfirmingDelete , setIsConfirmingDelete ] = useState ( false ) ;
38
+ const [ selectedInstrumentId , setSelectedInstrumentId ] = useState < string | undefined > ( ) ;
39
+
40
+ const {
41
+ checkoutState : {
42
+ errors : { getDeleteInstrumentError } ,
43
+ statuses : { isDeletingInstrument, isLoadingInstruments } ,
44
+ } ,
45
+ checkoutService : { deleteInstrument, clearError } ,
46
+ } = useCheckout ( ) ;
47
+
48
+ const deleteInstrumentError = getDeleteInstrumentError ( ) ;
49
+
50
+ const handleAfterOpen = ( ) : void => {
51
+ setIsConfirmingDelete ( false ) ;
52
+ onAfterOpen ?.( ) ;
43
53
} ;
44
54
45
- render ( ) : ReactNode {
46
- if ( ! this . context ) {
47
- throw Error ( 'Need to wrap in checkout context' ) ;
48
- }
49
-
50
- const {
51
- checkoutState : {
52
- errors : { getDeleteInstrumentError } ,
53
- } ,
54
- } = this . context ;
55
+ const handleCancel = ( ) : void => {
56
+ const existingError = getDeleteInstrumentError ( ) ;
55
57
56
- const deleteInstrumentError = getDeleteInstrumentError ( ) ;
58
+ if ( existingError ) {
59
+ void clearError ( existingError ) ;
60
+ }
57
61
58
- const { isOpen, onRequestClose } = this . props ;
62
+ setIsConfirmingDelete ( false ) ;
63
+ } ;
59
64
60
- return (
61
- < Modal
62
- closeButtonLabel = { < TranslatedString id = "common.close_action" /> }
63
- footer = { this . renderFooter ( ) }
64
- header = {
65
- < ModalHeader >
66
- < TranslatedString id = "payment.instrument_manage_modal_title_text" />
67
- </ ModalHeader >
68
- }
69
- isOpen = { isOpen }
70
- onAfterOpen = { this . handleAfterOpen }
71
- onRequestClose = { onRequestClose }
72
- >
73
- { deleteInstrumentError && < ManageInstrumentsAlert error = { deleteInstrumentError } /> }
65
+ const handleConfirmDelete = async ( ) : Promise < void > => {
66
+ if ( ! selectedInstrumentId ) {
67
+ return ;
68
+ }
74
69
75
- { this . renderContent ( ) }
76
- </ Modal >
77
- ) ;
78
- }
70
+ try {
71
+ await deleteInstrument ( selectedInstrumentId ) ;
72
+ onDeleteInstrument ( selectedInstrumentId ) ;
73
+ onRequestClose ?.( ) ;
74
+ } catch ( error ) {
75
+ const err = error instanceof Error ? error : new Error ( String ( error ) ) ;
79
76
80
- private renderContent ( ) : ReactNode {
81
- if ( ! this . context ) {
82
- throw Error ( 'Need to wrap in checkout context' ) ;
77
+ onDeleteInstrumentError ( err ) ;
83
78
}
79
+ } ;
84
80
85
- const {
86
- checkoutState : {
87
- statuses : { isDeletingInstrument } ,
88
- } ,
89
- } = this . context ;
90
- const { instruments } = this . props ;
91
-
92
- const { isConfirmingDelete } = this . state ;
81
+ const handleDeleteInstrument = ( id : string ) : void => {
82
+ setIsConfirmingDelete ( true ) ;
83
+ setSelectedInstrumentId ( id ) ;
84
+ } ;
93
85
86
+ const ModalContent = ( ) => {
94
87
if ( isConfirmingDelete ) {
95
88
return (
96
89
< p >
@@ -109,7 +102,7 @@ class ManageInstrumentsModal extends Component<
109
102
< ManageAchInstrumentsTable
110
103
instruments = { achInstrument }
111
104
isDeletingInstrument = { isDeletingInstrument ( ) }
112
- onDeleteInstrument = { this . handleDeleteInstrument }
105
+ onDeleteInstrument = { handleDeleteInstrument }
113
106
/>
114
107
) ;
115
108
}
@@ -121,7 +114,7 @@ class ManageInstrumentsModal extends Component<
121
114
< ManageAccountInstrumentsTable
122
115
instruments = { bankAndAccountInstruments }
123
116
isDeletingInstrument = { isDeletingInstrument ( ) }
124
- onDeleteInstrument = { this . handleDeleteInstrument }
117
+ onDeleteInstrument = { handleDeleteInstrument }
125
118
/>
126
119
) ;
127
120
}
@@ -130,129 +123,60 @@ class ManageInstrumentsModal extends Component<
130
123
< ManageCardInstrumentsTable
131
124
instruments = { cardInstruments }
132
125
isDeletingInstrument = { isDeletingInstrument ( ) }
133
- onDeleteInstrument = { this . handleDeleteInstrument }
126
+ onDeleteInstrument = { handleDeleteInstrument }
134
127
/>
135
128
) ;
136
- }
137
-
138
- private renderFooter ( ) : ReactNode {
139
- if ( ! this . context ) {
140
- throw Error ( 'Need to wrap in checkout context' ) ;
141
- }
142
-
143
- const {
144
- checkoutState : {
145
- statuses : { isDeletingInstrument, isLoadingInstruments } ,
146
- } ,
147
- } = this . context ;
148
-
149
- const { onRequestClose } = this . props ;
150
- const { isConfirmingDelete } = this . state ;
151
-
152
- if ( isConfirmingDelete ) {
153
- return (
154
- < >
155
- < Button
156
- onClick = { this . handleCancel }
157
- size = { ButtonSize . Small }
158
- testId = "manage-instrument-cancel-button"
159
- >
160
- < TranslatedString id = "common.cancel_action" />
161
- </ Button >
162
-
163
- < Button
164
- disabled = { isDeletingInstrument ( ) || isLoadingInstruments ( ) }
165
- onClick = { this . handleConfirmDelete }
166
- size = { ButtonSize . Small }
167
- testId = "manage-instrument-confirm-button"
168
- variant = { ButtonVariant . Primary }
169
- >
170
- < TranslatedString id = "payment.instrument_manage_modal_confirmation_action" />
171
- </ Button >
172
- </ >
173
- ) ;
174
- }
129
+ } ;
175
130
176
- return (
131
+ const ConfirmDelete = ( ) => (
132
+ < >
177
133
< Button
178
- onClick = { onRequestClose }
134
+ onClick = { handleCancel }
179
135
size = { ButtonSize . Small }
180
- testId = "manage-instrument-close -button"
136
+ testId = "manage-instrument-cancel -button"
181
137
>
182
- < TranslatedString id = "common.close_action " />
138
+ < TranslatedString id = "common.cancel_action " />
183
139
</ Button >
184
- ) ;
185
- }
186
140
187
- private handleAfterOpen : ( ) => void = ( ) => {
188
- const { onAfterOpen } = this . props ;
189
-
190
- this . setState (
191
- {
192
- isConfirmingDelete : false ,
193
- } ,
194
- onAfterOpen ,
195
- ) ;
196
- } ;
197
-
198
- private handleCancel : ( ) => void = ( ) => {
199
- if ( ! this . context ) {
200
- throw Error ( 'Need to wrap in checkout context' ) ;
201
- }
202
-
203
- const {
204
- checkoutState : {
205
- errors : { getDeleteInstrumentError } ,
206
- } ,
207
- checkoutService : { clearError } ,
208
- } = this . context ;
209
-
210
- const deleteInstrumentError = getDeleteInstrumentError ( ) ;
211
-
212
- if ( deleteInstrumentError ) {
213
- void clearError ( deleteInstrumentError ) ;
214
- }
215
-
216
- this . setState ( {
217
- isConfirmingDelete : false ,
218
- } ) ;
219
- } ;
220
-
221
- private handleConfirmDelete : ( ) => void = async ( ) => {
222
- if ( ! this . context ) {
223
- throw Error ( 'Need to wrap in checkout context' ) ;
224
- }
225
-
226
- const {
227
- checkoutService : { deleteInstrument } ,
228
- } = this . context ;
229
-
230
- const {
231
- onDeleteInstrument = noop ,
232
- onDeleteInstrumentError = noop ,
233
- onRequestClose = noop ,
234
- } = this . props ;
235
- const { selectedInstrumentId } = this . state ;
236
-
237
- if ( ! selectedInstrumentId ) {
238
- return ;
239
- }
240
-
241
- try {
242
- await deleteInstrument ( selectedInstrumentId ) ;
243
- onDeleteInstrument ( selectedInstrumentId ) ;
244
- onRequestClose ( ) ;
245
- } catch ( error ) {
246
- onDeleteInstrumentError ( error ) ;
247
- }
248
- } ;
249
-
250
- private handleDeleteInstrument : ( id : string ) => void = ( id ) => {
251
- this . setState ( {
252
- isConfirmingDelete : true ,
253
- selectedInstrumentId : id ,
254
- } ) ;
255
- } ;
256
- }
141
+ < Button
142
+ disabled = { isDeletingInstrument ( ) || isLoadingInstruments ( ) }
143
+ onClick = { handleConfirmDelete }
144
+ size = { ButtonSize . Small }
145
+ testId = "manage-instrument-confirm-button"
146
+ variant = { ButtonVariant . Primary }
147
+ >
148
+ < TranslatedString id = "payment.instrument_manage_modal_confirmation_action" />
149
+ </ Button >
150
+ </ >
151
+ ) ;
152
+ const CloseButton = ( ) => (
153
+ < Button
154
+ onClick = { onRequestClose }
155
+ size = { ButtonSize . Small }
156
+ testId = "manage-instrument-close-button"
157
+ >
158
+ < TranslatedString id = "common.close_action" />
159
+ </ Button >
160
+ ) ;
161
+
162
+ return (
163
+ < Modal
164
+ closeButtonLabel = { < TranslatedString id = "common.close_action" /> }
165
+ footer = { isConfirmingDelete ? < ConfirmDelete /> : < CloseButton /> }
166
+ header = {
167
+ < ModalHeader >
168
+ < TranslatedString id = "payment.instrument_manage_modal_title_text" />
169
+ </ ModalHeader >
170
+ }
171
+ isOpen = { isOpen }
172
+ onAfterOpen = { handleAfterOpen }
173
+ onRequestClose = { onRequestClose }
174
+ >
175
+ { deleteInstrumentError && < ManageInstrumentsAlert error = { deleteInstrumentError } /> }
176
+
177
+ < ModalContent />
178
+ </ Modal >
179
+ ) ;
180
+ } ;
257
181
258
182
export default ManageInstrumentsModal ;
0 commit comments