1- import '../sass/styles.scss' ;
2- import React from 'react' ;
3- import RedLayout from '../layouts/red' ;
4- import Hero from '../components/hero.js' ;
5- import { useHubspotForm } from '@aaronhayes/react-use-hubspot-form' ;
6- import emailIcon from '../files/icons/email.png' ;
7- import mailIcon from '../files/icons/mail.png' ;
8- import phoneIcon from '../files/icons/phone.png' ;
9- import SEO from '../components/seo' ;
1+ import "../sass/styles.scss" ;
2+ import React , { useState , useEffect } from "react" ;
3+ import RedLayout from "../layouts/red" ;
4+ import Hero from "../components/hero.js" ;
5+ import emailIcon from "../files/icons/email.png" ;
6+ import mailIcon from "../files/icons/mail.png" ;
7+ import phoneIcon from "../files/icons/phone.png" ;
8+ import SEO from "../components/seo" ;
9+
10+ const WORKER_ENDPOINT =
11+ "https://worker.civicactions.com/api/contact" ;
12+
13+ const TURNSTILE_SITE_KEY = "0x4AAAAAACbPfzYoEubkjDMX" ;
1014
1115const Contact = ( ) => {
12- const { loaded, error, formCreated } = useHubspotForm ( {
13- portalId : '9391823' ,
14- formId : '38c64225-614c-4786-876f-12d3174cb330' ,
15- target : '#contact-hubspot-form' ,
16- } ) ;
17- const fontStyles = {
18- color : 'white' ,
19- } ;
16+
17+ const [ status , setStatus ] = useState ( "idle" ) ;
18+ const [ errorMsg , setErrorMsg ] = useState ( "" ) ;
19+
20+ /*
21+ * LOAD TURNSTILE ONLY ONCE
22+ */
23+ useEffect ( ( ) => {
24+
25+ if ( ! document . querySelector ( 'script[src*="turnstile"]' ) ) {
26+ const script = document . createElement ( "script" ) ;
27+ script . src = "https://challenges.cloudflare.com/turnstile/v0/api.js" ;
28+ script . async = true ;
29+ script . defer = true ;
30+ document . body . appendChild ( script ) ;
31+ }
32+
33+ } , [ ] ) ;
34+
35+ async function handleSubmit ( e ) {
36+ e . preventDefault ( ) ;
37+
38+ setStatus ( "submitting" ) ;
39+ setErrorMsg ( "" ) ;
40+
41+ const token = window . turnstile ?. getResponse ( ) ;
42+
43+ if ( ! token ) {
44+ setStatus ( "error" ) ;
45+ setErrorMsg ( "Please complete verification." ) ;
46+ return ;
47+ }
48+
49+ const formData = new FormData ( e . target ) ;
50+
51+ const payload = {
52+ name : formData . get ( "name" ) ,
53+ email : formData . get ( "email" ) ,
54+ company : formData . get ( "organization" ) ,
55+ message : formData . get ( "message" ) ,
56+ "cf-turnstile-response" : token
57+ } ;
58+
59+ try {
60+
61+ const res = await fetch ( WORKER_ENDPOINT , {
62+ method : "POST" ,
63+ headers : { "Content-Type" : "application/json" } ,
64+ body : JSON . stringify ( payload )
65+ } ) ;
66+
67+ if ( ! res . ok ) {
68+ throw new Error ( await res . text ( ) ) ;
69+ }
70+
71+ setStatus ( "success" ) ;
72+ e . target . reset ( ) ;
73+ window . turnstile ?. reset ( ) ;
74+
75+ } catch ( err ) {
76+ console . error ( err ) ;
77+ setStatus ( "error" ) ;
78+ setErrorMsg ( "Something went wrong. Please try again." ) ;
79+ }
80+ }
81+
82+ const fontStyles = { color : "white" } ;
2083
2184 return (
2285 < RedLayout >
86+
2387 < Hero
24- title = ' Put us to work'
25- description = ' We love solving hard problems. Tell us about your challenge or idea, and we’ll get in touch to discuss a plan of action.'
88+ title = " Put us to work"
89+ description = " We love solving hard problems. Tell us about your challenge or idea, and we’ll get in touch."
2690 />
2791
28- < section className = 'contact--form' >
29- < div className = 'inner' >
30- < h2 className = 'visually-hidden' > Contact form by Hubspot</ h2 >
31- < div id = 'contact-hubspot-form' > </ div >
92+ < section className = "contact--form" >
93+ < div className = "inner" >
94+
95+ < h2 > Contact Us</ h2 >
96+
97+ < form onSubmit = { handleSubmit } noValidate >
98+
99+ < div className = "form-group" >
100+ < label htmlFor = "name" > Name</ label >
101+ < input id = "name" name = "name" type = "text" required autoComplete = "name" />
102+ </ div >
103+
104+ < div className = "form-group" >
105+ < label htmlFor = "email" > Email</ label >
106+ < input id = "email" name = "email" type = "email" required autoComplete = "email" />
107+ </ div >
108+
109+ < div className = "form-group" >
110+ < label htmlFor = "organization" > Organization (optional)</ label >
111+ < input id = "organization" name = "organization" type = "text" autoComplete = "organization" />
112+ </ div >
113+
114+ < div className = "form-group" >
115+ < label htmlFor = "message" > Message</ label >
116+ < textarea id = "message" name = "message" required rows = { 6 } />
117+ </ div >
118+
119+ < div
120+ className = "cf-turnstile"
121+ data-sitekey = { TURNSTILE_SITE_KEY }
122+ />
123+
124+ { status === "error" && (
125+ < p role = "alert" > { errorMsg } </ p >
126+ ) }
127+
128+ { status === "success" && (
129+ < p role = "status" >
130+ Thanks — your message has been sent!
131+ </ p >
132+ ) }
133+
134+ < button
135+ type = "submit"
136+ disabled = { status === "submitting" }
137+ >
138+ { status === "submitting"
139+ ? "Sending..."
140+ : "Send Message" }
141+ </ button >
142+
143+ </ form >
144+
32145 </ div >
33146 </ section >
34147
35- < section className = 'contact--ways-to-contact--section' >
36- < div className = 'inner' >
37- < h2 className = 'h3' style = { fontStyles } >
148+ < section className = "contact--ways-to-contact--section" >
149+ < div className = "inner" >
150+
151+ < h2 className = "h3" style = { fontStyles } >
38152 More ways to reach us
39153 </ h2 >
40- < p className = 'body' style = { fontStyles } >
41- Feel free to connect with us about work opportunities, speaking
42- engagements, potential partnerships, or just to say hi.
154+
155+ < p className = "body" style = { fontStyles } >
156+ Feel free to connect with us about work opportunities,
157+ speaking engagements, partnerships, or just to say hi.
43158 </ p >
44- < div className = 'contact-grid' >
45- < div className = 'grid' >
46- < img src = { emailIcon } alt = 'email icon' > </ img >
47- < p className = 'label body' style = { fontStyles } >
48- EMAIL
49- </ p >
50- < a
51- style = { fontStyles }
52- href = 'mailto:contact@civicactions.com'
53- className = 'link body' >
159+
160+ < div className = "contact-grid" >
161+
162+ < div className = "grid" >
163+ < img src = { emailIcon } alt = "" />
164+ < p className = "label body" style = { fontStyles } > EMAIL</ p >
165+ < a href = "mailto:contact@civicactions.com" className = "link body" style = { fontStyles } >
54166 contact@civicactions.com
55167 </ a >
56168 </ div >
57- < div className = 'grid' >
58- < img src = { mailIcon } alt = 'mail icon' > </ img >
59- < p style = { fontStyles } className = 'label body' >
60- MAIL
61- </ p >
62- < a
63- style = { fontStyles }
64- href = 'https://www.google.com/maps/place/3527+Mt+Diablo+Blvd+%23269,+Lafayette,+CA+94549,+USA/data=!4m2!3m1!1s0x8085625abdbe2b5d:0x1ab127226da2131c?sa=X& ved = 2 ahUKEwjvoPTJ7cTwAhVGT6wKHRJAAssQ8gEwAHoECAUQAQ '
65- className = 'link body' >
169+
170+ < div className = "grid" >
171+ < img src = { mailIcon } alt = "" />
172+ < p className = "label body" style = { fontStyles } > MAIL</ p >
173+ < a href = "https://www.google.com/maps/place/3527+Mt+Diablo+Blvd+%23269,+Lafayette,+CA+94549" className = "link body" style = { fontStyles } >
66174 3527 Mt Diablo Blvd #269 Lafayette, CA 94549
67175 </ a >
68176 </ div >
69- < div className = 'grid' >
70- < img src = { phoneIcon } alt = 'phone icon' > </ img >
71- < p style = { fontStyles } className = 'label body' >
72- PHONE
73- </ p >
74- < a
75- style = { fontStyles }
76- href = 'tel:510-408-7510'
77- className = 'link body' >
177+
178+ < div className = "grid" >
179+ < img src = { phoneIcon } alt = "" />
180+ < p className = "label body" style = { fontStyles } > PHONE</ p >
181+ < a href = "tel:510-408-7510" className = "link body" style = { fontStyles } >
78182 (510) 408-7510
79183 </ a >
80184 </ div >
185+
81186 </ div >
82187 </ div >
83188 </ section >
84189
85-
86190 </ RedLayout >
87191 ) ;
88192} ;
89193
90194export default Contact ;
91195
92196export const Head = ( ) => (
93- < SEO title = ' Contact' description = ' Get in touch.' />
94- ) ;
197+ < SEO title = " Contact" description = " Get in touch." />
198+ ) ;
0 commit comments