@@ -46,6 +46,15 @@ interface ISearchState {
4646 emailModalOpen : boolean ;
4747 emailSending : boolean ;
4848 emailStatus : string ;
49+ emailConfirming : boolean ;
50+ emailCount : number | null ;
51+ emailResult : null | {
52+ ok : boolean ;
53+ success : number ;
54+ failed : number ;
55+ statusLabel : string ;
56+ errorMessage ?: string ;
57+ } ;
4958}
5059
5160class SearchContainer extends React . Component < { } , ISearchState > {
@@ -61,6 +70,9 @@ class SearchContainer extends React.Component<{}, ISearchState> {
6170 emailModalOpen : false ,
6271 emailSending : false ,
6372 emailStatus : '' ,
73+ emailConfirming : false ,
74+ emailCount : null ,
75+ emailResult : null ,
6476 } ;
6577
6678 this . onFilterChange = this . onFilterChange . bind ( this ) ;
@@ -70,44 +82,106 @@ class SearchContainer extends React.Component<{}, ISearchState> {
7082 this . onSearchBarChanged = this . onSearchBarChanged . bind ( this ) ;
7183 this . openEmailModal = this . openEmailModal . bind ( this ) ;
7284 this . closeEmailModal = this . closeEmailModal . bind ( this ) ;
73- this . handleSendEmails = this . handleSendEmails . bind ( this ) ;
85+ this . startEmailConfirmation = this . startEmailConfirmation . bind ( this ) ;
86+ this . backFromEmailConfirmation = this . backFromEmailConfirmation . bind ( this ) ;
87+ this . confirmSendEmails = this . confirmSendEmails . bind ( this ) ;
88+ this . closeEmailResult = this . closeEmailResult . bind ( this ) ;
7489 this . state = {
7590 ...this . state ,
7691 emailModalOpen : false ,
7792 emailSending : false ,
7893 emailStatus : '' ,
94+ emailConfirming : false ,
95+ emailCount : null ,
96+ emailResult : null ,
7997 } ;
8098 }
8199 openEmailModal ( ) {
82100 this . setState ( { emailModalOpen : true } ) ;
83101 }
84102
85103 closeEmailModal ( ) {
86- this . setState ( { emailModalOpen : false , emailStatus : '' } ) ;
104+ this . setState ( {
105+ emailModalOpen : false ,
106+ emailStatus : '' ,
107+ emailConfirming : false ,
108+ emailCount : null ,
109+ emailSending : false ,
110+ emailResult : null ,
111+ } ) ;
87112 }
88113
89- async handleSendEmails ( status : string ) {
90- this . setState ( { emailSending : true , emailStatus : status } ) ;
114+ async startEmailConfirmation ( status : string ) {
91115 try {
92- const resp = await Emails . sendAutomatedStatus ( status ) ;
93- // Axios returns the response directly
94- const { success, failed } = resp . data . data ;
116+ // Fetch the count of emails to be sent
117+ const countResp = await Emails . getStatusCount ( status ) ;
118+ const count = countResp . data . data . count ;
119+ this . setState ( {
120+ emailStatus : status ,
121+ emailConfirming : true ,
122+ emailCount : typeof count === 'number' ? count : 0 ,
123+ } ) ;
124+ } catch ( err : any ) {
125+ const message = err ?. data ?. message || err ?. message || err ;
95126 alert (
96- `Successfully sent ${ success } ${ status . toLowerCase ( ) } emails` +
97- ( failed > 0 ? ` (${ failed } failed)` : '' )
127+ `Failed to retrieve ${ status . toLowerCase ( ) } email count: ${ message } `
98128 ) ;
129+ }
130+ }
131+
132+ backFromEmailConfirmation ( ) {
133+ this . setState ( {
134+ emailConfirming : false ,
135+ emailCount : null ,
136+ emailStatus : '' ,
137+ } ) ;
138+ }
139+
140+ async confirmSendEmails ( ) {
141+ const { emailStatus } = this . state ;
142+ if ( ! emailStatus ) return ;
143+ try {
144+ this . setState ( { emailSending : true } ) ;
145+ const resp = await Emails . sendAutomatedStatus ( emailStatus ) ;
146+ const { success, failed } = resp . data . data ;
147+ this . setState ( {
148+ emailResult : {
149+ ok : true ,
150+ success,
151+ failed,
152+ statusLabel : emailStatus ,
153+ } ,
154+ } ) ;
99155 } catch ( err : any ) {
100156 const message = err ?. data ?. message || err ?. message || err ;
101- alert ( `Failed to send ${ status . toLowerCase ( ) } emails: ${ message } ` ) ;
157+ this . setState ( {
158+ emailResult : {
159+ ok : false ,
160+ success : 0 ,
161+ failed : 0 ,
162+ statusLabel : emailStatus ,
163+ errorMessage : String ( message ) ,
164+ } ,
165+ } ) ;
102166 } finally {
103167 this . setState ( {
104168 emailSending : false ,
105- emailModalOpen : false ,
106- emailStatus : '' ,
169+ emailConfirming : false ,
107170 } ) ;
108171 }
109172 }
110173
174+ private closeEmailResult ( ) {
175+ this . setState ( {
176+ emailResult : null ,
177+ emailModalOpen : false ,
178+ emailStatus : '' ,
179+ emailConfirming : false ,
180+ emailCount : null ,
181+ emailSending : false ,
182+ } ) ;
183+ }
184+
111185 public render ( ) {
112186 const { searchBar, account, query, loading, viewSaved } = this . state ;
113187 return (
@@ -221,70 +295,141 @@ class SearchContainer extends React.Component<{}, ISearchState> {
221295 } }
222296 onClick = { ( e ) => e . stopPropagation ( ) }
223297 >
224- < h2 > Send Decision Emails</ h2 >
225- < div
226- style = { { display : 'flex' , flexDirection : 'column' , gap : 12 } }
227- >
228- < Button
229- disabled = { this . state . emailSending }
230- onClick = { ( ) => this . handleSendEmails ( 'Accepted' ) }
231- style = { {
232- backgroundColor : theme . colors . white ,
233- transition : 'background-color 0.2s' ,
234- } }
235- onMouseEnter = { ( e ) => {
236- e . currentTarget . style . backgroundColor =
237- theme . colors . black10 ;
238- } }
239- onMouseLeave = { ( e ) => {
240- e . currentTarget . style . backgroundColor = theme . colors . white ;
241- } }
242- >
243- Send All Acceptance Emails
244- </ Button >
245- < Button
246- disabled = { this . state . emailSending }
247- onClick = { ( ) => this . handleSendEmails ( 'Declined' ) }
248- style = { {
249- backgroundColor : theme . colors . white ,
250- transition : 'background-color 0.2s' ,
251- } }
252- onMouseEnter = { ( e ) => {
253- e . currentTarget . style . backgroundColor =
254- theme . colors . black10 ;
255- } }
256- onMouseLeave = { ( e ) => {
257- e . currentTarget . style . backgroundColor = theme . colors . white ;
258- } }
298+ { this . state . emailResult ? (
299+ < div
300+ style = { { display : 'flex' , flexDirection : 'column' , gap : 12 } }
259301 >
260- Send All Declined Emails
261- </ Button >
262- </ div >
263- < div
264- style = { {
265- display : 'flex' ,
266- justifyContent : 'center' ,
267- marginTop : 24 ,
268- } }
269- >
270- < Button
271- onClick = { this . closeEmailModal }
272- variant = { ButtonVariant . Secondary }
273- isOutlined = { true }
274- disabled = { this . state . emailSending }
275- onMouseEnter = { ( e ) => {
276- e . currentTarget . style . backgroundColor =
277- theme . colors . black10 ;
278- } }
279- onMouseLeave = { ( e ) => {
280- e . currentTarget . style . backgroundColor = theme . colors . white ;
281- } }
302+ < h2 >
303+ { this . state . emailResult . ok ? 'Emails Sent' : 'Send Failed' }
304+ </ h2 >
305+ { this . state . emailResult . ok ? (
306+ < p style = { { marginTop : 4 } } >
307+ Successfully sent{ ' ' }
308+ < strong > { this . state . emailResult . success } </ strong > { ' ' }
309+ { this . state . emailResult . statusLabel . toLowerCase ( ) } email
310+ { this . state . emailResult . success === 1 ? '' : 's' }
311+ { this . state . emailResult . failed > 0
312+ ? ` (${ this . state . emailResult . failed } failed)`
313+ : '' }
314+ .
315+ </ p >
316+ ) : (
317+ < >
318+ < p style = { { marginTop : 4 } } >
319+ Failed to send{ ' ' }
320+ { this . state . emailResult . statusLabel . toLowerCase ( ) } { ' ' }
321+ emails.
322+ </ p >
323+ < p
324+ style = { {
325+ color : theme . colors . black60 ,
326+ whiteSpace : 'pre-wrap' ,
327+ } }
328+ >
329+ { this . state . emailResult . errorMessage }
330+ </ p >
331+ </ >
332+ ) }
333+ < div style = { { display : 'flex' , gap : 12 , marginTop : 8 } } >
334+ < Button onClick = { this . closeEmailResult } > Done</ Button >
335+ </ div >
336+ </ div >
337+ ) : this . state . emailConfirming ? (
338+ < div
339+ style = { { display : 'flex' , flexDirection : 'column' , gap : 12 } }
282340 >
283- Cancel
284- </ Button >
285- </ div >
286-
287- { this . state . emailSending && < p > Sending emails...</ p > }
341+ < h2 > Confirm Send</ h2 >
342+ < p style = { { marginTop : 4 } } >
343+ You are about to send{ ' ' }
344+ < strong > { this . state . emailCount ?? 0 } </ strong > { ' ' }
345+ { this . state . emailStatus . toLowerCase ( ) } email
346+ { this . state . emailCount === 1 ? '' : 's' } .
347+ </ p >
348+ < p style = { { color : theme . colors . black60 , marginTop : 0 } } >
349+ This will email all hackers currently marked as{ ' ' }
350+ < strong > { this . state . emailStatus } </ strong > .
351+ </ p >
352+ < div style = { { display : 'flex' , gap : 12 , marginTop : 8 } } >
353+ < Button
354+ disabled = {
355+ this . state . emailSending ||
356+ ( this . state . emailCount ?? 0 ) === 0
357+ }
358+ onClick = { this . confirmSendEmails }
359+ variant = { ButtonVariant . Primary }
360+ >
361+ { this . state . emailSending ? 'Sending…' : 'Confirm Send' }
362+ </ Button >
363+ < Button
364+ onClick = { this . backFromEmailConfirmation }
365+ variant = { ButtonVariant . Secondary }
366+ isOutlined = { true }
367+ disabled = { this . state . emailSending }
368+ >
369+ Back
370+ </ Button >
371+ </ div >
372+ </ div >
373+ ) : (
374+ < >
375+ < h2 > Send Decision Emails</ h2 >
376+ < div
377+ style = { {
378+ display : 'flex' ,
379+ flexDirection : 'column' ,
380+ gap : 12 ,
381+ } }
382+ >
383+ < Button
384+ disabled = { this . state . emailSending }
385+ onClick = { ( ) => this . startEmailConfirmation ( 'Accepted' ) }
386+ variant = { ButtonVariant . Secondary }
387+ isOutlined = { true }
388+ onMouseEnter = { ( e ) => {
389+ e . currentTarget . style . backgroundColor =
390+ theme . colors . black5 ;
391+ } }
392+ onMouseLeave = { ( e ) => {
393+ e . currentTarget . style . backgroundColor =
394+ theme . colors . white ;
395+ } }
396+ >
397+ Send All Acceptance Emails
398+ </ Button >
399+ < Button
400+ disabled = { this . state . emailSending }
401+ onClick = { ( ) => this . startEmailConfirmation ( 'Declined' ) }
402+ variant = { ButtonVariant . Secondary }
403+ isOutlined = { true }
404+ onMouseEnter = { ( e ) => {
405+ e . currentTarget . style . backgroundColor =
406+ theme . colors . black5 ;
407+ } }
408+ onMouseLeave = { ( e ) => {
409+ e . currentTarget . style . backgroundColor =
410+ theme . colors . white ;
411+ } }
412+ >
413+ Send All Declined Emails
414+ </ Button >
415+ </ div >
416+ < div
417+ style = { {
418+ display : 'flex' ,
419+ justifyContent : 'center' ,
420+ marginTop : 24 ,
421+ } }
422+ >
423+ < Button
424+ onClick = { this . closeEmailModal }
425+ variant = { ButtonVariant . Primary }
426+ disabled = { this . state . emailSending }
427+ >
428+ Cancel
429+ </ Button >
430+ </ div >
431+ </ >
432+ ) }
288433 </ div >
289434 </ div >
290435 ) }
0 commit comments