1- import React , { useState , useRef , useEffect } from 'react' ;
1+ import React , { useState } from 'react' ;
22import { FormikProvider , useFormik } from 'formik' ;
33import { useBoolean } from 'ahooks' ;
44import isEmpty from 'lodash/isEmpty' ;
55import { Link } from '@app/components/Link' ;
6- // import { subscribeUser } from '@app/components/SubscriptionForm/utils';
7- import { createBemBlockBuilder } from '@app/utils' ;
6+ import { subscribeUser } from '@app/components/SubscriptionForm/utils' ;
7+ import { createBemBlockBuilder , CONTACT_US_URL } from '@app/utils' ;
8+ import { useRecaptcha } from '@app/hooks/useRecaptcha' ;
89import axios from 'axios' ;
910
1011import { validate , getBaseSalesForceValues } from './utils' ;
@@ -19,16 +20,11 @@ import '../ContactUsPage.scss';
1920
2021const getBlocksWith = createBemBlockBuilder ( [ 'contact-us-form' ] ) ;
2122
22- const MIN_FORM_INTERACTION_TIME = 3000 ;
23-
2423export const ContactUsForm = ( { title, options, isDiscussFieldShown } ) => {
2524 const [ isFeedbackFormVisible , { setTrue : showFeedbackForm } ] = useBoolean ( false ) ;
2625 const [ isLoading , setIsLoading ] = useState ( false ) ;
27- const formMountTimeRef = useRef < number | null > ( null ) ;
28-
29- useEffect ( ( ) => {
30- formMountTimeRef . current = Date . now ( ) ;
31- } , [ ] ) ;
26+ const [ customError , setCustomError ] = useState < string | null > ( null ) ;
27+ const { executeRecaptcha, recaptchaError, clearError } = useRecaptcha ( ) ;
3228 const formik = useFormik ( {
3329 initialValues : {
3430 first_name : '' ,
@@ -37,54 +33,67 @@ export const ContactUsForm = ({ title, options, isDiscussFieldShown }) => {
3733 company : '' ,
3834 termsAgree : false ,
3935 wouldLikeToReceiveAds : false ,
40- website : '' , // Honeypot field - should remain empty
4136 ...( isDiscussFieldShown && { discuss : '' } ) ,
4237 } ,
4338 validateOnBlur : false ,
4439 validateOnChange : false ,
4540 validate,
4641 onSubmit : async values => {
47- // Bot detection: Check honeypot field
48- if ( values . website ) {
49- console . warn ( 'Bot detected: honeypot field filled' ) ;
42+ if ( isLoading ) {
43+ return ;
44+ }
45+
46+ const errors = await validateForm ( ) ;
47+
48+ if ( ! isEmpty ( errors ) ) {
5049 return ;
5150 }
5251
53- // Bot detection: Check if form was submitted too quickly
54- if ( formMountTimeRef . current !== null ) {
55- const timeSinceMount = Date . now ( ) - formMountTimeRef . current ;
56- if ( timeSinceMount < MIN_FORM_INTERACTION_TIME ) {
57- console . warn ( 'Bot detected: form submitted too quickly' ) ;
52+ try {
53+ setIsLoading ( true ) ;
54+ clearError ( ) ;
55+ setCustomError ( null ) ;
56+
57+ const contactRecaptchaToken = await executeRecaptcha ( ) ;
58+ if ( ! contactRecaptchaToken || recaptchaError ) {
5859 return ;
5960 }
60- }
6161
62- validateForm ( ) . then ( errors => {
63- if ( isEmpty ( errors ) ) {
64- setIsLoading ( true ) ;
65-
66- const baseSalesForceValues = getBaseSalesForceValues ( options ) ;
67- // Remove honeypot field before submitting
68- // eslint-disable-next-line @typescript-eslint/no-unused-vars
69- const { website, ...cleanValues } = values ;
70- const postData = {
71- ...cleanValues ,
72- ...baseSalesForceValues ,
73- } ;
74-
75- // if (values.wouldLikeToReceiveAds) {
76- // subscribeUser(values.email).catch(console.error);
77- // }
78-
79- axios
80- . post ( `${ process . env . CONTACT_US_URL } ` , postData )
81- . catch ( console . error )
82- . finally ( ( ) => {
83- showFeedbackForm ( ) ;
84- setIsLoading ( false ) ;
85- } ) ;
62+ const baseSalesForceValues = getBaseSalesForceValues ( options ) ;
63+ const postData = {
64+ ...values ,
65+ ...baseSalesForceValues ,
66+ } ;
67+
68+ if ( values . wouldLikeToReceiveAds ) {
69+ const subscribeRecaptchaToken = await executeRecaptcha ( ) ;
70+ if ( subscribeRecaptchaToken ) {
71+ subscribeUser ( values . email , subscribeRecaptchaToken ) . catch ( console . error ) ;
72+ }
73+ }
74+
75+ const headers = {
76+ 'Content-Type' : 'application/json' ,
77+ 'RP-Recaptcha-Action' : 'contact_us' ,
78+ ...( contactRecaptchaToken && { 'RP-Recaptcha-Token' : contactRecaptchaToken } ) ,
79+ } ;
80+
81+ const response = await axios . post ( CONTACT_US_URL , postData , { headers } ) ;
82+
83+ let responseData = response . data ;
84+ if ( typeof responseData === 'string' ) {
85+ responseData = JSON . parse ( responseData ) ;
86+ }
87+
88+ if ( responseData . success ) {
89+ showFeedbackForm ( ) ;
90+ } else {
91+ setIsLoading ( false ) ;
8692 }
87- } ) ;
93+ } catch ( error ) {
94+ setCustomError ( 'Request failed. Please try again.' ) ;
95+ setIsLoading ( false ) ;
96+ }
8897 } ,
8998 } ) ;
9099 const { getFieldProps, validateForm } = formik ;
@@ -107,20 +116,6 @@ export const ContactUsForm = ({ title, options, isDiscussFieldShown }) => {
107116 maxLength = { 80 }
108117 />
109118 < FormInput name = "company" label = "Company name" placeholder = "ABC" maxLength = { MAX_LENGTH } />
110- { /* Honeypot field - hidden from users but visible to bots */ }
111- < div
112- style = { { position : 'absolute' , left : '-9999px' , opacity : 0 , pointerEvents : 'none' } }
113- aria-hidden = "true"
114- >
115- < FormInput
116- name = "website"
117- label = "Website"
118- placeholder = "https://example.com"
119- maxLength = { MAX_LENGTH }
120- tabIndex = { - 1 }
121- autoComplete = "off"
122- />
123- </ div >
124119 { isDiscussFieldShown && (
125120 < FormInput
126121 name = "discuss"
@@ -130,9 +125,9 @@ export const ContactUsForm = ({ title, options, isDiscussFieldShown }) => {
130125 maxLength = { MAX_LENGTH }
131126 />
132127 ) }
133- { /* <FormFieldWrapper name="wouldLikeToReceiveAds"> */ }
134- { /* <CustomCheckbox label="Subscribe to ReportPortal newsletter" /> */ }
135- { /* </FormFieldWrapper> */ }
128+ < FormFieldWrapper name = "wouldLikeToReceiveAds" >
129+ < CustomCheckbox label = "Subscribe to ReportPortal newsletter" />
130+ </ FormFieldWrapper >
136131 < FormFieldWrapper name = "termsAgree" >
137132 < CustomCheckbox
138133 label = {
@@ -146,13 +141,16 @@ export const ContactUsForm = ({ title, options, isDiscussFieldShown }) => {
146141 }
147142 />
148143 </ FormFieldWrapper >
144+ { ( recaptchaError || customError ) && (
145+ < div className = "recaptcha-error" > { recaptchaError || customError } </ div >
146+ ) }
149147 < button
150148 className = "btn btn--primary btn--large"
151149 type = "submit"
152150 data-gtm = "send_request"
153151 disabled = { ! getFieldProps ( 'termsAgree' ) . value || isLoading }
154152 >
155- Send request
153+ { isLoading ? 'Sending...' : ' Send request' }
156154 </ button >
157155 </ form >
158156 </ div >
0 commit comments