| 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; |