Skip to content

Commit cbf93a0

Browse files
authored
Merge pull request #1359 from Adez017/testimonial
Added Contact us Email Functionality
2 parents dd451dc + f6da8bf commit cbf93a0

7 files changed

Lines changed: 220 additions & 70 deletions

File tree

.env.example

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ SHOPIFY_STOREFRONT_ACCESS_TOKEN=your_storefront_access_token_here
2020
# FIREBASE_AUTH_DOMAIN=your_firebase_auth_domain
2121
# FIREBASE_PROJECT_ID=your_firebase_project_id
2222

23+
# EmailJS Configuration (for Contact Us page)
24+
# Sign up at https://www.emailjs.com and create a service + template
25+
# Only the public key, service ID, and template ID are exposed to the browser
26+
EMAILJS_PUBLIC_KEY=your_emailjs_public_key
27+
EMAILJS_SERVICE_ID=your_emailjs_service_id
28+
EMAILJS_TEMPLATE_ID=your_emailjs_template_id
29+
# The private API key is for server-side REST API use only — do NOT expose it in the browser
30+
# EMAILJS_PRIVATE_KEY=your_emailjs_private_key
31+
2332
# Analytics Configuration
2433
# GOOGLE_ANALYTICS_ID=your_google_analytics_id
2534
# VERCEL_ANALYTICS_ID=your_vercel_analytics_id

docusaurus.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,10 @@ const config: Config = {
294294
SHOPIFY_STOREFRONT_ACCESS_TOKEN:
295295
process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN ||
296296
"2503dfbf93132b42e627e7d53b3ba3e9",
297+
// EmailJS credentials for Contact Us page (public values only)
298+
EMAILJS_PUBLIC_KEY: process.env.EMAILJS_PUBLIC_KEY || "",
299+
EMAILJS_SERVICE_ID: process.env.EMAILJS_SERVICE_ID || "",
300+
EMAILJS_TEMPLATE_ID: process.env.EMAILJS_TEMPLATE_ID || "",
297301
hooks: {
298302
onBrokenMarkdownLinks: "warn",
299303
},

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"@docusaurus/theme-classic": "^3.9.2",
3636
"@docusaurus/theme-mermaid": "3.9.1",
3737
"@docusaurus/theme-search-algolia": "3.9.2",
38+
"@emailjs/browser": "^4.4.1",
3839
"@floating-ui/react": "^0.27.8",
3940
"@giscus/react": "^3.1.0",
4041
"@mdx-js/react": "^3.0.0",

src/pages/contact-us/index.css

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,52 @@ html[data-theme="light"] {
482482
transform: translateY(0) scale(0.98);
483483
}
484484

485+
.submit-button:disabled {
486+
opacity: 0.7;
487+
cursor: not-allowed;
488+
transform: none;
489+
}
490+
491+
/* Form feedback messages */
492+
.form-success-message {
493+
display: flex;
494+
flex-direction: column;
495+
align-items: center;
496+
justify-content: center;
497+
text-align: center;
498+
gap: 0.75rem;
499+
padding: 2rem 1rem;
500+
}
501+
502+
.form-success-icon {
503+
font-size: 3rem;
504+
}
505+
506+
.form-success-message h3 {
507+
font-size: 1.5rem;
508+
font-weight: 700;
509+
color: var(--contact-text-primary);
510+
margin: 0;
511+
}
512+
513+
.form-success-message p {
514+
color: var(--contact-text-muted);
515+
margin: 0;
516+
}
517+
518+
.form-success-button {
519+
margin-top: 1rem;
520+
}
521+
522+
.form-error-message {
523+
padding: 0.875rem 1rem;
524+
background: rgba(239, 68, 68, 0.1);
525+
border: 1px solid rgba(239, 68, 68, 0.3);
526+
border-radius: 0.75rem;
527+
color: #ef4444;
528+
font-size: 0.9375rem;
529+
}
530+
485531
/* Additional Resources */
486532
.contact-resources {
487533
text-align: center;

src/pages/contact-us/index.tsx

Lines changed: 149 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,56 @@
1-
import React from "react";
1+
import React, { useRef, useState } from "react";
22
import Layout from "@theme/Layout";
3+
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
4+
import emailjs from "@emailjs/browser";
35
import { Mail, MapPin, Clock } from "lucide-react";
46
import "./index.css";
57

8+
type FormStatus = "idle" | "sending" | "success" | "error";
9+
610
const ContactUs: React.FC = () => {
11+
const { siteConfig } = useDocusaurusContext();
12+
const formRef = useRef<HTMLFormElement>(null);
13+
const [status, setStatus] = useState<FormStatus>("idle");
14+
const [errorMessage, setErrorMessage] = useState("");
15+
16+
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
17+
e.preventDefault();
18+
if (!formRef.current) return;
19+
20+
const publicKey = siteConfig.customFields?.EMAILJS_PUBLIC_KEY as string;
21+
const serviceId = siteConfig.customFields?.EMAILJS_SERVICE_ID as string;
22+
const templateId = siteConfig.customFields?.EMAILJS_TEMPLATE_ID as string;
23+
24+
if (!publicKey || !serviceId || !templateId) {
25+
setErrorMessage(
26+
"Email service is not configured. Please contact us directly at sanjay@recodehive.com.",
27+
);
28+
setStatus("error");
29+
return;
30+
}
31+
32+
setStatus("sending");
33+
setErrorMessage("");
34+
35+
try {
36+
await emailjs.sendForm(serviceId, templateId, formRef.current, {
37+
publicKey,
38+
});
39+
setStatus("success");
40+
formRef.current.reset();
41+
} catch (err) {
42+
console.error("EmailJS send error:", err);
43+
const detail =
44+
err && typeof err === "object" && "text" in err
45+
? ` (${(err as { status?: number; text?: string }).status ?? ""}: ${(err as { text?: string }).text ?? ""})`
46+
: "";
47+
setErrorMessage(
48+
`Something went wrong${detail}. Please try again or email us directly at sanjay@recodehive.com.`,
49+
);
50+
setStatus("error");
51+
}
52+
};
53+
754
return (
855
<Layout
956
title="Contact Us"
@@ -123,88 +170,121 @@ const ContactUs: React.FC = () => {
123170
<div className="contact-form-section">
124171
<h2 className="contact-form-title">Send us a message</h2>
125172

126-
<form className="contact-form">
127-
<div className="form-row">
173+
{status === "success" ? (
174+
<div className="form-success-message">
175+
<div className="form-success-icon"></div>
176+
<h3>Message Sent!</h3>
177+
<p>
178+
Thank you for reaching out. We'll get back to you within
179+
24-48 hours.
180+
</p>
181+
<button
182+
className="submit-button form-success-button"
183+
onClick={() => setStatus("idle")}
184+
>
185+
Send Another Message
186+
</button>
187+
</div>
188+
) : (
189+
<form
190+
ref={formRef}
191+
className="contact-form"
192+
onSubmit={handleSubmit}
193+
>
194+
<div className="form-row">
195+
<div className="form-group">
196+
<label htmlFor="firstName" className="form-label">
197+
First Name
198+
</label>
199+
<input
200+
type="text"
201+
id="firstName"
202+
name="firstName"
203+
className="form-input"
204+
placeholder="Your first name"
205+
required
206+
/>
207+
</div>
208+
<div className="form-group">
209+
<label htmlFor="lastName" className="form-label">
210+
Last Name
211+
</label>
212+
<input
213+
type="text"
214+
id="lastName"
215+
name="lastName"
216+
className="form-input"
217+
placeholder="Your last name"
218+
required
219+
/>
220+
</div>
221+
</div>
222+
128223
<div className="form-group">
129-
<label htmlFor="firstName" className="form-label">
130-
First Name
224+
<label htmlFor="email" className="form-label">
225+
Email Address
131226
</label>
132227
<input
133-
type="text"
134-
id="firstName"
135-
name="firstName"
228+
type="email"
229+
id="email"
230+
name="email"
136231
className="form-input"
137-
placeholder="Your first name"
232+
placeholder="your.email@example.com"
138233
required
139234
/>
140235
</div>
236+
141237
<div className="form-group">
142-
<label htmlFor="lastName" className="form-label">
143-
Last Name
238+
<label htmlFor="subject" className="form-label">
239+
Subject
144240
</label>
145-
<input
146-
type="text"
147-
id="lastName"
148-
name="lastName"
149-
className="form-input"
150-
placeholder="Your last name"
241+
<select
242+
id="subject"
243+
name="subject"
244+
className="form-select"
151245
required
152-
/>
246+
>
247+
<option value="">Select a subject</option>
248+
<option value="general">General Inquiry</option>
249+
<option value="support">Technical Support</option>
250+
<option value="collaboration">Collaboration</option>
251+
<option value="partnership">Partnership</option>
252+
<option value="feedback">Feedback</option>
253+
<option value="other">Other</option>
254+
</select>
153255
</div>
154-
</div>
155-
156-
<div className="form-group">
157-
<label htmlFor="email" className="form-label">
158-
Email Address
159-
</label>
160-
<input
161-
type="email"
162-
id="email"
163-
name="email"
164-
className="form-input"
165-
placeholder="your.email@example.com"
166-
required
167-
/>
168-
</div>
169256

170-
<div className="form-group">
171-
<label htmlFor="subject" className="form-label">
172-
Subject
173-
</label>
174-
<select
175-
id="subject"
176-
name="subject"
177-
className="form-select"
178-
required
179-
>
180-
<option value="">Select a subject</option>
181-
<option value="general">General Inquiry</option>
182-
<option value="support">Technical Support</option>
183-
<option value="collaboration">Collaboration</option>
184-
<option value="partnership">Partnership</option>
185-
<option value="feedback">Feedback</option>
186-
<option value="other">Other</option>
187-
</select>
188-
</div>
257+
<div className="form-group">
258+
<label htmlFor="message" className="form-label">
259+
Message
260+
</label>
261+
<textarea
262+
id="message"
263+
name="message"
264+
rows={6}
265+
className="form-textarea"
266+
placeholder="Tell us more about your inquiry..."
267+
required
268+
></textarea>
269+
</div>
189270

190-
<div className="form-group">
191-
<label htmlFor="message" className="form-label">
192-
Message
193-
</label>
194-
<textarea
195-
id="message"
196-
name="message"
197-
rows={6}
198-
className="form-textarea"
199-
placeholder="Tell us more about your inquiry..."
200-
required
201-
></textarea>
202-
</div>
271+
{status === "error" && (
272+
<div className="form-error-message">{errorMessage}</div>
273+
)}
203274

204-
<button type="submit" className="submit-button">
205-
Send Message
206-
</button>
207-
</form>
275+
<button
276+
type="submit"
277+
className="submit-button"
278+
disabled={status === "sending"}
279+
aria-busy={status === "sending"}
280+
aria-label={
281+
status === "sending" ? "Sending message…" : "Send message"
282+
}
283+
>
284+
{status === "sending" ? "Sending…" : "Send Message"}
285+
</button>
286+
</form>
287+
)}
208288
</div>
209289
</div>
210290

tsconfig.tsbuildinfo

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)