-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathsample.tsx
182 lines (171 loc) · 8.6 KB
/
sample.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import React, { useState, SyntheticEvent } from "react"
import { Helmet } from "react-helmet"
import { CheckCircleIcon, XIcon, ExclamationCircleIcon } from '@heroicons/react/outline'
const CLOUDFLARE_WORKER_URL = "https://your-url-goes-here.cloudflare1234.workers.dev"
const GOOGLE_CAPTCHA_KEY = "your-google-captcha-key-goes-here"
const ContactPage = () => {
const [name, setName] = useState("")
const [email, setEmail] = useState("")
const [message, setMessage] = useState("")
const [success, setSuccess] = useState(false)
const [error, setError] = useState(false)
const [errorMessage, setErrorMessage] = useState("")
const [showAlert, setShowAlert] = useState(false)
const sendEmailFailedMsg = 'Request failed to send. We have logged the error. Please try again later if you have not heard from us in 24 hours.';
const handleSubmit = (e: SyntheticEvent<HTMLElement>) => {
e.preventDefault()
window.grecaptcha.ready(async function () {
const token = await window.grecaptcha.execute(GOOGLE_CAPTCHA_KEY, { action: 'submit' });
const body = {
from: {
name: name,
email: email,
},
message: message,
token: token
};
try {
const request = await fetch(CLOUDFLARE_WORKER_URL, {
method: "POST",
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json"
}
})
if (request.status === 202) {
setShowAlert(true)
setSuccess(true)
setError(false)
setErrorMessage('')
} else {
const text = await request.text();
setShowAlert(true)
setSuccess(false)
setError(true)
setErrorMessage(text)
}
} catch (error) {
setShowAlert(true)
setSuccess(false)
setError(true)
setErrorMessage(sendEmailFailedMsg)
}
})
}
return (
<div>
<Helmet title="Contact" defer={false}>
<script async src={`https://www.google.com/recaptcha/api.js?render=${GOOGLE_CAPTCHA_KEY}`}></script>
</Helmet>
{showAlert && error ? <Alert success={false} message={errorMessage} onDismiss={() => setShowAlert(false)} /> : ''}
{showAlert && success ? <Alert success={true} message='Message sent!' onDismiss={() => setShowAlert(false)} /> : ''}
<div className="rounded overflow-hidden shadow-lg py-6 px-6">
<h1 className="text-5xl font-bold mb-4">Contact Us</h1>
<div className="mt-12">
<form onSubmit={e => handleSubmit(e)} action="#" method="POST" className="grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-8">
<div>
<label htmlFor="name" className="block text-sm font-medium text-gray-700">
Name
</label>
<div className="mt-1">
<input
type="text"
name="name"
id="name"
autoComplete="given-name"
className="py-3 px-4 block w-full shadow-sm focus:ring-green-500 focus:border-green-500 border-gray-300 border rounded-md"
onChange={(e) => setName(e.target.value)}
/>
</div>
</div>
<div className="sm:col-span-2">
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
Email
</label>
<div className="mt-1">
<input
id="email"
name="email"
type="email"
autoComplete="email"
className="py-3 px-4 block w-full shadow-sm focus:ring-green-500 focus:border-green-500 border-gray-300 border rounded-md"
onChange={(e) => setEmail(e.target.value)}
/>
</div>
</div>
<div className="sm:col-span-2">
<label htmlFor="message" className="block text-sm font-medium text-gray-700">
Message
</label>
<div className="mt-1">
<textarea
id="message"
name="message"
rows={4}
className="py-3 px-4 block w-full shadow-sm focus:ring-green-500 focus:border-green-500 border-gray-300 border rounded-md"
defaultValue={''}
onChange={(e) => setMessage(e.target.value)}
/>
</div>
</div>
<div className="sm:col-span-2">
<button
type="submit"
disabled={showAlert}
className={`w-full inline-flex items-center justify-center px-6 py-3 border border-transparent rounded-md shadow-sm text-base font-medium text-white focus:outline-none focus:ring-2 bg-green-900 focus:ring-offset-2 focus:ring-green-00 ${showAlert ? 'opacity-50 cursor-not-allowed' : 'opacity-100 cursor-pointer hover:bg-green-700'}`}
>
Let's talk
</button>
</div>
</form>
</div>
</div>
</div>
)
}
function Alert(props: React.PropsWithChildren<{ success: boolean, message: string, onDismiss: () => void }>) {
return (
<div className="fixed bottom-0 inset-x-0 pb-2 sm:pb-5">
<div className="max-w-7xl mx-auto px-2 sm:px-6 lg:px-8">
<div className={`p-2 rounded-lg ${props.success ? 'bg-green-600' : 'bg-red-600'} shadow-lg sm:p-3`}>
<div className="flex items-center justify-between flex-wrap">
<div className="w-0 flex-1 flex items-center">
{
props.success ?
<span className={`flex p-2 rounded-lg ${props.success ? 'bg-green-800' : 'bg-red-800'}`}>
<CheckCircleIcon className="h-6 w-6 text-white" aria-hidden="true" />
</span>
:
<span className={`flex p-2 rounded-lg ${props.success ? 'bg-green-800' : 'bg-red-800'}`}>
<ExclamationCircleIcon className="h-6 w-6 text-white" aria-hidden="true" />
</span>
}
<p className="ml-3 font-medium text-white truncate">
<span className="inline">{props.message}</span>
</p>
</div>
<div className="order-2 flex-shrink-0 sm:order-3 sm:ml-2">
<button
type="button"
className={`-mr-1 flex p-2 rounded-md hover:bg-color-500 focus:outline-none focus:ring-2 focus:ring-white`}
onClick={props.onDismiss}
>
<span className="sr-only">Dismiss</span>
<XIcon className="h-6 w-6 text-white" aria-hidden="true" />
</button>
</div>
</div>
</div>
</div>
</div>
)
}
export default ContactPage
export interface ContactRequest {
from: Contact
message: string
}
export interface Contact {
name: string
email: string
}