|
| 1 | +'use client'; |
| 2 | + |
| 3 | +import React, { useMemo, useState } from 'react'; |
| 4 | +import { useForm } from 'react-hook-form'; |
| 5 | +import { createHubSpotTicket } from '@/lib/hubspotAPI'; |
| 6 | +import { CircularProgress, Snackbar } from '@mui/material'; |
| 7 | +import Checkbox from '@/components/icons/checkbox'; |
| 8 | + |
| 9 | +function Form() { |
| 10 | + const [isLoading, setIsLoading] = useState(false); |
| 11 | + const { |
| 12 | + register, |
| 13 | + handleSubmit, |
| 14 | + formState: { errors }, |
| 15 | + trigger, |
| 16 | + } = useForm<{ |
| 17 | + firstName: string; |
| 18 | + lastName: string; |
| 19 | + email: string; |
| 20 | + company?: string; |
| 21 | + message: string; |
| 22 | + }>({ |
| 23 | + mode: 'onChange', |
| 24 | + }); |
| 25 | + |
| 26 | + const [message, setMessage] = useState({ |
| 27 | + open: false, |
| 28 | + type: '', |
| 29 | + }); |
| 30 | + const fields: { |
| 31 | + name: 'firstName' | 'lastName' | 'email' | 'company' | 'message'; |
| 32 | + label: string; |
| 33 | + type: 'text' | 'email' | 'textarea'; |
| 34 | + required?: boolean; |
| 35 | + }[] = useMemo(() => { |
| 36 | + return [ |
| 37 | + { |
| 38 | + name: 'firstName', |
| 39 | + label: 'First name', |
| 40 | + type: 'text', |
| 41 | + required: true, |
| 42 | + }, |
| 43 | + { |
| 44 | + name: 'lastName', |
| 45 | + label: 'Last name', |
| 46 | + type: 'text', |
| 47 | + required: true, |
| 48 | + }, |
| 49 | + { |
| 50 | + name: 'email', |
| 51 | + label: 'Business email', |
| 52 | + type: 'email', |
| 53 | + required: true, |
| 54 | + }, |
| 55 | + { |
| 56 | + name: 'company', |
| 57 | + label: 'Company name', |
| 58 | + type: 'text', |
| 59 | + }, |
| 60 | + { |
| 61 | + name: 'message', |
| 62 | + label: 'Message or question', |
| 63 | + type: 'textarea', |
| 64 | + required: true, |
| 65 | + }, |
| 66 | + ]; |
| 67 | + }, []); |
| 68 | + |
| 69 | + const closeMessage = () => { |
| 70 | + setMessage({ |
| 71 | + open: false, |
| 72 | + type: '', |
| 73 | + }); |
| 74 | + }; |
| 75 | + |
| 76 | + return ( |
| 77 | + <form |
| 78 | + id={'contact-form'} |
| 79 | + onSubmit={handleSubmit(async (formData) => { |
| 80 | + setIsLoading(true); |
| 81 | + try { |
| 82 | + await createHubSpotTicket(formData); |
| 83 | + setMessage({ |
| 84 | + open: true, |
| 85 | + type: 'success', |
| 86 | + }); |
| 87 | + } catch (error) { |
| 88 | + setMessage({ |
| 89 | + open: true, |
| 90 | + type: 'error', |
| 91 | + }); |
| 92 | + } |
| 93 | + |
| 94 | + setIsLoading(false); |
| 95 | + })} |
| 96 | + > |
| 97 | + {fields.map((field) => { |
| 98 | + return ( |
| 99 | + <div key={field.name} className={'form-group'}> |
| 100 | + <div className={'form-input'}> |
| 101 | + {field.type === 'textarea' ? ( |
| 102 | + <textarea |
| 103 | + id={field.name} |
| 104 | + rows={5} |
| 105 | + placeholder={field.label} |
| 106 | + {...register(field.name, { required: field.required })} |
| 107 | + ></textarea> |
| 108 | + ) : ( |
| 109 | + <input |
| 110 | + type={field.type} |
| 111 | + id={field.name} |
| 112 | + placeholder={field.label} |
| 113 | + {...register(field.name, { |
| 114 | + required: field.required, |
| 115 | + pattern: field.type === 'email' ? /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ : undefined, |
| 116 | + })} |
| 117 | + /> |
| 118 | + )} |
| 119 | + |
| 120 | + {field.required && ( |
| 121 | + <div className={'required'}> |
| 122 | + <svg xmlns='http://www.w3.org/2000/svg' width='14' height='15' viewBox='0 0 14 15' fill='none'> |
| 123 | + <path |
| 124 | + fillRule='evenodd' |
| 125 | + clipRule='evenodd' |
| 126 | + d='M7.875 0.5H6.125V5.38786L2.66914 1.932L1.4317 3.16943L4.88727 6.625H0V8.375H4.88871L1.43157 11.8321L2.66901 13.0696L6.125 9.61359V14.5H7.875V9.61273L11.3312 13.0689L12.5686 11.8315L9.11214 8.375H14V6.625H9.11359L12.5685 3.17009L11.3311 1.93265L7.875 5.38871V0.5Z' |
| 127 | + fill='#9327FF' |
| 128 | + /> |
| 129 | + </svg> |
| 130 | + </div> |
| 131 | + )} |
| 132 | + </div> |
| 133 | + |
| 134 | + {/* errors will return when field validation fails */} |
| 135 | + {errors[field.name]?.type === 'required' && <div className={'error'}>{field.label} is required</div>} |
| 136 | + {errors[field.name]?.type === 'pattern' && <div className={'error'}>{field.label} is invalid</div>} |
| 137 | + </div> |
| 138 | + ); |
| 139 | + })} |
| 140 | + |
| 141 | + <button |
| 142 | + onClick={() => { |
| 143 | + trigger('firstName'); |
| 144 | + }} |
| 145 | + className={'download-btn'} |
| 146 | + type={'submit'} |
| 147 | + disabled={isLoading} |
| 148 | + > |
| 149 | + {isLoading && <CircularProgress size={24} color='secondary' />} |
| 150 | + <span className={'mr-1'}>Send</span> |
| 151 | + <svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'> |
| 152 | + <path d='M1.5 1.5H10.5M10.5 1.5V10.5M10.5 1.5L1.5 10.5' stroke='white' strokeWidth='1.8' /> |
| 153 | + </svg> |
| 154 | + </button> |
| 155 | + <Snackbar |
| 156 | + className={'contact-message'} |
| 157 | + anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} |
| 158 | + open={message.open} |
| 159 | + autoHideDuration={6000} |
| 160 | + onClose={closeMessage} |
| 161 | + > |
| 162 | + <div className={'contact-message-content'}> |
| 163 | + {message.type !== '' && ( |
| 164 | + <> |
| 165 | + <div className={'contact-message-icon'}> |
| 166 | + {message.type === 'success' ? ( |
| 167 | + <Checkbox /> |
| 168 | + ) : ( |
| 169 | + <svg xmlns='http://www.w3.org/2000/svg' width='100%' height='100%' viewBox='0 0 19 19' fill='none'> |
| 170 | + <path |
| 171 | + fillRule='evenodd' |
| 172 | + clipRule='evenodd' |
| 173 | + d='M9.5 1.35714C5.00311 1.35714 1.35714 5.00311 1.35714 9.5C1.35714 13.9989 5.00311 17.6429 9.5 17.6429C13.9969 17.6429 17.6429 13.9989 17.6429 9.5C17.6429 5.00311 13.9969 1.35714 9.5 1.35714ZM0 9.5C0 4.25329 4.25329 0 9.5 0C14.7467 0 19 4.25329 19 9.5C19 14.7454 14.7467 19 9.5 19C4.25329 19 0 14.7454 0 9.5ZM5.58533 5.62741C5.84998 5.36209 6.27951 5.36209 6.54483 5.62741L9.5 8.58258L12.4552 5.62741C12.7205 5.36209 13.15 5.36209 13.4147 5.62741C13.68 5.89206 13.68 6.32223 13.4147 6.58687L10.4595 9.54208L13.4147 12.4993C13.68 12.7639 13.68 13.1914 13.4147 13.4561C13.15 13.7207 12.7205 13.7207 12.4552 13.4561L9.5 10.5016L6.54483 13.4561C6.27951 13.7207 5.84998 13.7207 5.58533 13.4561C5.32001 13.1914 5.32001 12.7639 5.58533 12.4993L8.5405 9.54208L5.58533 6.58687C5.32001 6.32223 5.32001 5.89206 5.58533 5.62741Z' |
| 174 | + fill='red' |
| 175 | + /> |
| 176 | + </svg> |
| 177 | + )} |
| 178 | + </div> |
| 179 | + <div className={'contact-message-text'}> |
| 180 | + {message.type === 'success' ? ( |
| 181 | + <> |
| 182 | + <div className={'contact-message-title'}>Thank you</div> |
| 183 | + <div className={'contact-message-desc'}> |
| 184 | + Your message has been sent and we will review it as soon as possible. |
| 185 | + </div> |
| 186 | + </> |
| 187 | + ) : ( |
| 188 | + <> |
| 189 | + <div className={'contact-message-title'}>Something went wrong</div> |
| 190 | + <div className={'contact-message-desc'}>Please try again later.</div> |
| 191 | + </> |
| 192 | + )} |
| 193 | + </div> |
| 194 | + </> |
| 195 | + )} |
| 196 | + </div> |
| 197 | + </Snackbar> |
| 198 | + </form> |
| 199 | + ); |
| 200 | +} |
| 201 | + |
| 202 | +export default Form; |
0 commit comments