|
import React, { useState, useEffect, useRef} from 'react'; |
|
import styled from 'styled-components'; |
|
import IntlTelInput from "intl-tel-input/reactWithUtils"; |
|
import "intl-tel-input/build/css/intlTelInput.css"; |
|
import axios from 'axios'; |
|
import privacyPolicyData from "../../data/privacyPolicyData"; |
|
import { showCustomAlert, showCustomError, showCustomSuccess } from '../../utils/showalerts'; |
|
|
|
import { DateTime } from 'luxon'; |
|
|
|
import DatePicker from "react-datepicker"; |
|
import "react-datepicker/dist/react-datepicker.css"; |
|
|
|
const Form = styled.form` |
|
display: flex; |
|
color: #f8f8f8; |
|
flex-direction: column; |
|
gap: 1rem; |
|
width: 80%; |
|
@media (max-width: 768px) { |
|
width: 90%; |
|
} |
|
align-self: center; |
|
justify-self: center; |
|
|
|
.iti__selected-dial-code{ |
|
color: black; |
|
} |
|
;` |
|
|
|
const Input = styled.input` |
|
width: 100%; |
|
padding: 0.75rem 1rem; |
|
font-size: 1rem; |
|
border: 3px solid ${(props) => (props.invalid ? '#de493e' : '#3267B9')}; |
|
border-radius: 0.375rem; |
|
background-origin: border-box; |
|
color: black; |
|
|
|
&:focus { |
|
outline: none; |
|
box-shadow: 0 0 8px rgba(63, 122, 211, 0.5); |
|
} |
|
;` |
|
|
|
const Textarea = styled.textarea` |
|
width: 100%; |
|
padding: 0.75rem 1rem; |
|
font-size: 1rem; |
|
border: 3px solid ${(props) => (props.invalid ? '#de493e' : '#3267B9')}; |
|
border-radius: 0.375rem; |
|
color: black; |
|
|
|
&:focus { |
|
outline: none; |
|
box-shadow: 0 0 8px rgba(63, 122, 211, 0.8); |
|
} |
|
;` |
|
|
|
const Select = styled.select` |
|
width: 100%; |
|
padding: 0.75rem 1rem; |
|
font-size: 1rem; |
|
border: 3px solid ${(props) => (props.invalid ? '#de493e' : '#3267B9')}; |
|
border-radius: 0.375rem; |
|
color: black; |
|
|
|
&:focus { |
|
outline: none; |
|
box-shadow: 0 0 8px rgba(63, 122, 211, 0.5); |
|
} |
|
;` |
|
|
|
const SubmitButton = styled.button` |
|
width: 100%; |
|
padding: 0.75rem 1.5rem; |
|
background: #3267B9; |
|
color: white; |
|
font-weight: 600; |
|
border-radius: 0.375rem; |
|
transition: background 0.3s; |
|
&:hover { |
|
background: #3f7ad3; |
|
} |
|
;` |
|
|
|
const ConsentWrapper = styled.div` |
|
display: grid; |
|
grid-template-columns: auto 1fr; |
|
gap: 0.5rem; |
|
color: #d5d5d5; |
|
;` |
|
|
|
|
|
const ContactForm = ({ formType, demoProduct }) => { |
|
const [name, setName] = useState(''); |
|
const [email, setEmail] = useState(''); |
|
const [phone, setPhone] = useState(''); |
|
const [isValid, setIsValid] = useState(false); |
|
const [subject, setSubject] = useState(''); |
|
const [message, setMessage] = useState(''); |
|
const [company, setCompany] = useState(''); |
|
const [product, setProduct] = useState('VarDiG AI'); |
|
const [demoDate, setDemoDate] = useState(''); |
|
const [additionalComments, setAdditionalComments] = useState(''); |
|
const [initCountry, setInitCountry] = useState('in'); |
|
const [minDemoDate, setMinDemoDate] = useState(''); |
|
const [maxDemoDate, setMaxDemoDate] = useState(''); |
|
const [consent, setConsent] = useState(false); |
|
const [selectedSlot, setSelectedSlot] = useState(''); |
|
const [availableDates, setAvailableDates] = useState([]); |
|
const [timezone, setTimezone] = useState([]); |
|
const [availableSlots, setAvailableSlots] = useState({}); |
|
const [slots, setSlots] = useState([]); |
|
const [formSubmitted, setFormSubmitted] = useState(false); |
|
|
|
const [errors, setErrors] = useState({}); |
|
const fieldRefs = useRef({ |
|
name: React.createRef(), |
|
email: React.createRef(), |
|
phone: React.createRef(), |
|
subject: React.createRef(), |
|
message: React.createRef(), |
|
company: React.createRef(), |
|
}); |
|
|
|
const validateName = (name) => name.length >= 3; |
|
const validateEmail = (email) => /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email); |
|
const validatePhone = () => isValid; |
|
const validateSubject = (subject) => subject.length >= 15; |
|
const validateMessage = (message) => message.length >= 15; |
|
const validateCompany = (company) => company.length >1; |
|
|
|
const handleInputChange = (e, setter, fieldName) => { |
|
setter(e.target.value); |
|
setErrors({}); |
|
}; |
|
|
|
const handleBlur = (fieldName) => { |
|
const validationErrors = {}; |
|
if (fieldName === 'name' && !validateName(name)) { |
|
validationErrors.name = 'Name should be at least 3 characters long.'; |
|
} |
|
if (fieldName === 'email' && !validateEmail(email)) { |
|
validationErrors.email = 'Invalid email format.'; |
|
} |
|
if (fieldName === 'phone' && !validatePhone()) { |
|
validationErrors.phone = 'Please enter a valid phone number.'; |
|
} |
|
if (fieldName ==='subject' && !validateSubject(subject)){ |
|
validationErrors.subject = `Subject is small and unclear.`; |
|
} |
|
if (fieldName ==='message' && !validateMessage(message)){ |
|
validationErrors.message = `Message is small and unclear.`; |
|
} |
|
if (fieldName ==='company' && !validateCompany(company)){ |
|
validationErrors.company = `Company/Institution Name is required.`; |
|
} |
|
setErrors(validationErrors); |
|
|
|
if (Object.keys(validationErrors).length > 0) { |
|
console.log(fieldRefs.current[fieldName]); |
|
fieldRefs.current[fieldName].current.focus(); |
|
} |
|
}; |
|
|
|
useEffect(() => { |
|
setProduct(demoProduct); |
|
}, [demoProduct]) |
|
|
|
useEffect(() => { |
|
const fetchCountryCode = async () => { |
|
try { |
|
const response = await axios.get('https://ipapi.co/json/'); |
|
setInitCountry(response.data.country_code.toLowerCase()); |
|
} catch (error) { |
|
setInitCountry('in'); |
|
} |
|
}; |
|
|
|
fetchCountryCode(); |
|
const minDate = DateTime.local().plus({ days: 2 }); |
|
setMinDemoDate(minDate); |
|
const zone = DateTime.local().zoneName; |
|
setTimezone(zone); |
|
|
|
}, []); |
|
|
|
useEffect(() => { |
|
if (minDemoDate) { |
|
const fetchAvailableDates = async () => { |
|
try { |
|
const requiredMinDate = minDemoDate; |
|
const response = await axios.post('/api/get-available-dates-and-slots', { requiredMinDate, timezone }); |
|
setAvailableDates(response.data.finalDates); |
|
setMaxDemoDate(response.data.finalDates[response.data.finalDates.length - 1]); |
|
setAvailableSlots(response.data.finalDateSlots); |
|
} catch (error) { |
|
console.log(error); |
|
} |
|
}; |
|
fetchAvailableDates(); |
|
} |
|
}, [minDemoDate, formSubmitted]); |
|
|
|
useEffect(() => { |
|
const handler = setTimeout(() => { |
|
const thedate = DateTime.fromJSDate(demoDate).toFormat('yyyy-MM-dd'); |
|
if (availableSlots && availableSlots[thedate]) { |
|
const reqslot = availableSlots[thedate]; |
|
setSlots(reqslot); |
|
} |
|
}, 300); |
|
|
|
return () => clearTimeout(handler); |
|
}, [demoDate]); |
|
|
|
|
|
|
|
const isAvailableDate = (date) => { |
|
const formattedDate = DateTime.fromJSDate(date).toFormat('yyyy-MM-dd'); |
|
return availableDates.includes(formattedDate); |
|
}; |
|
|
|
const handleSubmit = async (e) => { |
|
e.preventDefault(); |
|
|
|
if (!consent) { |
|
showCustomError("You must consent to the processing of your personal data."); |
|
return; |
|
} |
|
|
|
|
|
const policyVersion = privacyPolicyData.version; |
|
|
|
let formData = |
|
formType === 'general' |
|
? { name, email, phone, subject, message, consent, policyVersion} |
|
: { name, email, phone, company, product, demoDate, selectedSlot, additionalComments, timezone, consent, policyVersion}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (demoDate) { |
|
const slotstart = selectedSlot.split('-')[0]; |
|
const modifiedDemoDate = DateTime.fromJSDate(demoDate).set({ hour: slotstart.split(':')[0], minute: slotstart.split(':')[1] }); |
|
formData = { |
|
...formData, |
|
demoDate: modifiedDemoDate, |
|
}; |
|
} |
|
|
|
|
|
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; |
|
if (!emailRegex.test(email)) { |
|
showCustomError("Please enter a valid email address."); |
|
return; |
|
} |
|
|
|
if (!isValid) { |
|
showCustomError("Please enter a valid phone number."); |
|
return; |
|
} |
|
|
|
try { |
|
const endpoint = |
|
formType === 'general' |
|
? '/api/submit-contact-form' |
|
: '/api/demo-request'; |
|
|
|
showCustomAlert("Please wait.... Your submission is in progress"); |
|
const response = await axios.post(endpoint, formData); |
|
|
|
if (response.status >= 200 && response.status < 300) { |
|
if (response.data.error === "Unable to send demo request confirmation email, however the demo request has been registered successfully!") { |
|
showCustomAlert(response.data.error); |
|
}else if (response.data.error === 'Unable to send Contact request confirmation email, however request registered successfully!') { |
|
showCustomAlert(response.data.error); |
|
}else if (response.data.error === "You have already requested a demo for this product within 15 days. Our team will get back to you shortly.") { |
|
showCustomAlert(response.data.error); |
|
} |
|
else{ |
|
showCustomSuccess(response.data.message); |
|
} |
|
|
|
|
|
|
|
setName(''); |
|
setEmail(''); |
|
setPhone(''); |
|
|
|
setSubject(''); |
|
setMessage(''); |
|
setCompany(''); |
|
setProduct('VarDiG SaaS'); |
|
setDemoDate(''); |
|
setSelectedSlot(''); |
|
setAdditionalComments(''); |
|
setConsent(false); |
|
|
|
setFormSubmitted(prev => !prev); |
|
} |
|
} catch (error) { |
|
const response = error.response; |
|
if (response.data.error === "You have already requested a demo for this product within 15 days. Our team will get back to you shortly.") { |
|
showCustomAlert(response.data.error); |
|
} else if (response.data.error === "Your contact request with same subject is in queue") { |
|
showCustomAlert(response.data.error); |
|
} else { |
|
showCustomError(response.data.error || response.statusText || "An unexpected error occurred. Please try again."); |
|
} |
|
|
|
|
|
setName(''); |
|
setEmail(''); |
|
setPhone(''); |
|
setSubject(''); |
|
setMessage(''); |
|
setCompany(''); |
|
setProduct('VarDiG SaaS'); |
|
setDemoDate(''); |
|
setSelectedSlot(''); |
|
setAdditionalComments(''); |
|
setConsent(false); |
|
} |
|
}; |
|
|
|
return ( |
|
<Form onSubmit={handleSubmit} data-testid='formFields' aria-label={`${formType}`}> |
|
<div style={{ color:"f8f8f8", display: "flex" }}> |
|
Name{" "} |
|
<div style={{ color: "red", marginLeft: "10px" }}>*</div> |
|
</div> |
|
<Input |
|
ref={fieldRefs.current.name} |
|
type="text" |
|
value={name} |
|
onChange={(e) => { |
|
// Only allow letters (including spaces and accented characters) |
|
const regex = /^[A-Za-z\s]*$/; |
|
if (regex.test(e.target.value) || e.target.value === '') { |
|
handleInputChange(e, setName, 'name'); |
|
} |
|
}} |
|
invalid={errors.name} |
|
onBlur={() => handleBlur('name')} |
|
placeholder="Your Name" |
|
required |
|
autoFocus |
|
/> |
|
{errors.name && <span style={{ color: '#de493e', fontSize: '0.875rem' }}>{errors.name}</span>} |
|
|
|
<div style={{ color:"f8f8f8", display: "flex" }}> |
|
E-Mail{" "} |
|
<div style={{ color: "red", marginLeft: "10px" }}>*</div> |
|
</div> |
|
<Input |
|
ref={fieldRefs.current.email} |
|
type="email" |
|
value={email} |
|
placeholder="Your Email" |
|
onChange={(e) => handleInputChange(e, setEmail, 'email')} |
|
invalid={errors.email} |
|
onBlur={() => handleBlur('email')} |
|
required |
|
/> |
|
{errors.email && <span style={{ color: '#de493e', fontSize: '0.875rem' }}>{errors.email}</span>} |
|
|
|
<div style={{ color:"f8f8f8", display: "flex" }}> |
|
Phone Number{" "} |
|
<div style={{ color: "red", marginLeft: "10px" }}>*</div> |
|
</div> |
|
<IntlTelInput invalid={errors.phone} |
|
ref={fieldRefs.current.phone} |
|
value={phone} |
|
onChangeNumber={setPhone} |
|
onChangeValidity={setIsValid} |
|
initOptions={{ |
|
initialCountry: initCountry, |
|
separateDialCode: true, |
|
}} |
|
inputProps={{ |
|
placeholder: 'Your Phone Number', |
|
required: true, |
|
}} |
|
onBlur={() => handleBlur('phone')} |
|
/> |
|
{errors.phone && <span style={{ color: '#de493e', fontSize: '0.875rem' }}>{errors.phone}</span>} |
|
|
|
{formType === 'general' ? ( |
|
<> |
|
<div style={{ color:"f8f8f8", display: "flex" }}> |
|
Subject{" "} |
|
<div style={{ color: "red", marginLeft: "10px" }}>*</div> |
|
</div> |
|
<Input invalid={errors.subject} |
|
ref={fieldRefs.current.subject} |
|
type="text" |
|
value={subject} |
|
onChange={(e) => handleInputChange(e, setSubject, 'subject')} |
|
placeholder="Subject" |
|
onBlur={() => handleBlur('subject')} |
|
required |
|
/> |
|
{errors.subject && <span style={{ color: '#de493e', fontSize: '0.875rem' }}>{errors.subject}</span>} |
|
<div style={{ color:"f8f8f8", display: "flex" }}> |
|
Message{" "} |
|
<div style={{ color: "red", marginLeft: "10px" }}>*</div> |
|
</div> |
|
<Textarea invalid={errors.message} |
|
ref={fieldRefs.current.message} |
|
value={message} |
|
onChange={(e) => handleInputChange(e, setMessage, 'message')} |
|
placeholder="Your Message" |
|
onBlur={() => handleBlur('message')} |
|
required |
|
/> |
|
{errors.message && <span style={{ color: '#de493e', fontSize: '0.875rem' }}>{errors.message}</span>} |
|
{/* Consent Checkbox */} |
|
<ConsentWrapper> |
|
<Input |
|
type="checkbox" |
|
id="gdpr-consent" |
|
checked={consent} |
|
onChange={() => setConsent(!consent)} |
|
required |
|
/> |
|
<label htmlFor="gdpr-consent"> |
|
I consent to the processing of my submitted data in accordance with the{' '} |
|
<a href="/privacy-policy" target="_blank" rel="noopener noreferrer"> |
|
<i style={{ color: "#3f7ad3" }}>Privacy Policy</i> |
|
</a>.{' '} |
|
<span style={{ color: "red", marginLeft: "5px" }}>*</span> |
|
</label> |
|
</ConsentWrapper> |
|
|
|
<SubmitButton type="submit">Submit Message</SubmitButton> |
|
</> |
|
) : ( |
|
<> |
|
<div style={{ color:"f8f8f8", display: "flex" }}> |
|
Company/Institution Name{" "} |
|
<div style={{ color: "red", marginLeft: "10px" }}>*</div> |
|
</div> |
|
<Input |
|
ref={fieldRefs.current.company} |
|
type="text" |
|
value={company} |
|
placeholder="Company/ Institution Name" |
|
onChange={(e) => handleInputChange(e, setCompany, 'company')} |
|
invalid={errors.company} |
|
onBlur={() => handleBlur('company')} |
|
required |
|
/> |
|
{errors.company && <span style={{ color: '#de493e', fontSize: '0.875rem' }}>{errors.company}</span>} |
|
|
|
<div style={{ color:"f8f8f8", display: "flex" }}> |
|
Select Product{" "} |
|
<div style={{ color: "red", marginLeft: "10px" }}>*</div> |
|
</div> |
|
<Select value={product} onChange={(e) => setProduct(e.target.value)} data-testid='product-combobox'> |
|
<option value="VarDiG SaaS">VarDiG SaaS</option> |
|
<option value="VarDiG AI">VarDiG AI</option> |
|
</Select> |
|
|
|
<div style={{ color:"f8f8f8", display: "flex" }}> |
|
Select Demo Date{" "} |
|
<div style={{ color: "red", marginLeft: "10px" }}>*</div> |
|
</div> |
|
<DatePicker |
|
showIcon |
|
selected={demoDate} |
|
onChange={(date) => setDemoDate(date)} |
|
minDate={minDemoDate} |
|
maxDate={maxDemoDate + 1} |
|
required |
|
onKeyDown={(e) => e.preventDefault()} |
|
filterDate={isAvailableDate} |
|
placeholderText="Select a Date" |
|
customInput={<Input />} |
|
icon={ |
|
<svg |
|
xmlns="http://www.w3.org/2000/svg" |
|
width="1em" |
|
height="1em" |
|
viewBox="0 0 48 48" |
|
> |
|
<mask id="ipSApplication0"> |
|
<g fill="none" stroke="#fff" strokeLinejoin="round" strokeWidth="4"> |
|
<path strokeLinecap="round" d="M40.04 22v20h-32V22"></path> |
|
<path |
|
fill="#fff" |
|
d="M5.842 13.777C4.312 17.737 7.263 22 11.51 22c3.314 0 6.019-2.686 6.019-6a6 6 0 0 0 6 6h1.018a6 6 0 0 0 6-6c0 3.314 2.706 6 6.02 6c4.248 0 7.201-4.265 5.67-8.228L39.234 6H8.845l-3.003 7.777Z" |
|
></path> |
|
</g> |
|
</mask> |
|
<path |
|
fill="currentColor" |
|
d="M0 0h48v48H0z" |
|
mask="url(#ipSApplication0)" |
|
></path> |
|
</svg> |
|
} |
|
/> |
|
{demoDate && ( |
|
<Select |
|
value={selectedSlot} |
|
onChange={(e) => setSelectedSlot(e.target.value)} |
|
required |
|
> |
|
|
|
<option value="">Select a Time Slot</option> |
|
{slots.map((slot, index) => ( |
|
<option key={index} value={slot}> |
|
{slot} |
|
</option> |
|
))} |
|
</Select> |
|
)} |
|
|
|
<div style={{ color:"f8f8f8", display: "flex" }}> |
|
Addiional Comments{" "} |
|
</div> |
|
<Textarea |
|
value={additionalComments} |
|
onChange={(e) => setAdditionalComments(e.target.value)} |
|
placeholder="Additional Comments" |
|
/> |
|
|
|
{/* Consent Checkbox */} |
|
<ConsentWrapper> |
|
<Input |
|
type="checkbox" |
|
id="gdpr-consent" |
|
checked={consent} |
|
onChange={() => setConsent(!consent)} |
|
required |
|
/> |
|
<label htmlFor="gdpr-consent"> |
|
I consent to the processing of my submitted data in accordance with the{' '} |
|
<a href="/privacy-policy" target="_blank" rel="noopener noreferrer"> |
|
<i style={{ color: "#3f7ad3" }}>Privacy Policy</i> |
|
</a>.{' '} |
|
<span style={{ color: "red", marginLeft: "5px" }}>*</span> |
|
</label> |
|
</ConsentWrapper> |
|
|
|
<SubmitButton type="submit">Submit Demo Request</SubmitButton> |
|
</> |
|
)} |
|
</Form> |
|
); |
|
}; |
|
|
|
export default ContactForm; |