-
Notifications
You must be signed in to change notification settings - Fork 63
integrated emailjs #208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
integrated emailjs #208
Conversation
✅ Deploy Preview for github-spy ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
WalkthroughAdded EmailJS dependency and integrated EmailJS sending in Contact form. Replaced simulated submission with emailjs.send using env-driven IDs. Implemented refs for inputs, wired refs to fields, handled success by showing popup and clearing fields, added basic error handling internally. No changes to exports or component signature. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant CF as ContactForm (React)
participant EJ as EmailJS SDK
participant ES as EmailJS Service
U->>CF: Click "Send"
CF->>CF: Read values via refs
alt Has env IDs
CF->>EJ: emailjs.send(serviceId, templateId, templateParams, publicKey)
EJ->>ES: Deliver request
ES-->>EJ: Response (success/failure)
alt Success
EJ-->>CF: Resolve
CF->>CF: Show success popup<br/>Clear fields<br/>Set submitting=false
else Failure
EJ-->>CF: Reject
CF->>CF: Log/handle error internally<br/>Set submitting=false
end
else Missing config
CF->>CF: Skip send / handle error
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉 Thank you @omkadu8767 for your contribution. Please make sure your PR follows https://github.com/GitMetricsLab/github_tracker/blob/main/CONTRIBUTING.md#-pull-request-guidelines
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (3)
src/pages/Contact/Contact.tsx (3)
36-41: Trim inputs before sending.Prevents accidental leading/trailing spaces from being included in the email.
Apply this diff:
- const templateParams = { - from_name: nameRef.current?.value || "", - from_email: emailRef.current?.value || "", - subject: subjectRef.current?.value || "", - message: messageRef.current?.value || "", - }; + const templateParams = { + from_name: (nameRef.current?.value || "").trim(), + from_email: (emailRef.current?.value || "").trim(), + subject: (subjectRef.current?.value || "").trim(), + message: (messageRef.current?.value || "").trim(), + };
55-59: Tie auto-close to popup visibility and clean up timer on unmount.Create the timeout when showPopup flips true; clear it on unmount to avoid stray state updates.
Apply this diff to remove the unconditional timeout:
- // Auto-close popup after 5 seconds - setTimeout(() => { - setShowPopup(false); - }, 5000);Add this effect near other hooks:
// Auto-close success popup after 5s useEffect(() => { if (!showPopup) return; const id = setTimeout(() => setShowPopup(false), 5000); return () => clearTimeout(id); }, [showPopup]);Also applies to: 336-356
17-18: Null-safe ThemeContext access (optional).If ThemeContext is ever null outside a provider, the cast could throw at runtime. Guard or provide a default.
Example:
const theme = useContext(ThemeContext); const mode = theme?.mode ?? "light";
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
package.json(1 hunks)src/pages/Contact/Contact.tsx(10 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pages/Contact/Contact.tsx (1)
src/context/ThemeContext.tsx (2)
ThemeContext(10-10)ThemeContextType(49-49)
🔇 Additional comments (1)
package.json (1)
15-15: EmailJS browser dependency addition looks good.Adding "@emailjs/browser" as a runtime dependency is appropriate for client-side EmailJS usage.
| // Refs for form fields | ||
| const nameRef = useRef<HTMLInputElement>(null); | ||
| const emailRef = useRef<HTMLInputElement>(null); | ||
| const subjectRef = useRef<HTMLSelectElement>(null); | ||
| const messageRef = useRef<HTMLTextAreaElement>(null); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Inputs are uncontrolled; Issue #203 requests controlled components.
The linked issue explicitly calls for useState-managed, controlled inputs with validation. Consider switching refs to state for consistency and easier validation UX.
Example (sketch, not a full diff):
// state
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [subject, setSubject] = useState("");
const [message, setMessage] = useState("");
// inputs
<input value={name} onChange={(e) => setName(e.target.value)} ... />
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} ... />
<select value={subject} onChange={(e) => setSubject(e.target.value)} required>...</select>
<textarea value={message} onChange={(e) => setMessage(e.target.value)} ... />
// template params
const templateParams = { from_name: name.trim(), from_email: email.trim(), subject: subject.trim(), message: message.trim() };
// clear after success
setName(""); setEmail(""); setSubject(""); setMessage("");If you want, I can generate a complete patch converting all four fields.
Also applies to: 234-242, 259-263, 278-285, 311-315, 36-41, 47-51
🤖 Prompt for AI Agents
In src/pages/Contact/Contact.tsx around lines 20-25 (also apply to 36-41, 47-51,
234-242, 259-263, 278-285, 311-315): replace the uncontrolled refs for name,
email, subject and message with useState-managed controlled inputs; create state
variables and setters for each field, bind each input/select/textarea value to
its state and add onChange handlers to update state, enforce validation
(required and email format) on inputs, when building templateParams use trimmed
state values, and after a successful submit reset all state fields to empty
strings; remove the refs and any direct DOM reads and ensure any existing logic
that cleared or referenced refs is updated to use the new state variables.
| const handleSubmit = async (e?: React.MouseEvent<HTMLButtonElement>) => { | ||
| if (e) e.preventDefault(); | ||
| setIsSubmitting(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Submit via form onSubmit to leverage native required-field validation.
Right now required attributes won’t prevent the button click. Hook the handler to the form’s onSubmit and make the button type="submit".
Apply this diff:
- const handleSubmit = async (e?: React.MouseEvent<HTMLButtonElement>) => {
- if (e) e.preventDefault();
+ const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
+ e.preventDefault();- <div className="space-y-3 sm:space-y-4 flex-1 flex flex-col">
+ <form onSubmit={handleSubmit} className="space-y-3 sm:space-y-4 flex-1 flex flex-col" noValidate>- <button
- onClick={handleSubmit}
- disabled={isSubmitting}
+ <button
+ type="submit"
+ disabled={isSubmitting}Also applies to: 221-221, 318-324
| // EmailJS config (replace with your own) | ||
| const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID; | ||
| const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID | ||
| const publicKey = import.meta.env.VITE_EMAILJS_USER_ID; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Fix env var name for EmailJS public key and guard missing config.
Use VITE_EMAILJS_PUBLIC_KEY (per your PR docs) instead of VITE_EMAILJS_USER_ID. Also bail out early if any config is missing to avoid silent failures.
Apply this diff:
- const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
- const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID
- const publicKey = import.meta.env.VITE_EMAILJS_USER_ID;
+ const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
+ const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID;
+ const publicKey = import.meta.env.VITE_EMAILJS_PUBLIC_KEY;
+
+ if (!serviceId || !templateId || !publicKey) {
+ // Surface a clear signal in dev and prevent the send call
+ console.error("Missing EmailJS configuration (serviceId/templateId/publicKey).");
+ setIsSubmitting(false);
+ return;
+ }Run to find inconsistent usages across the repo (and docs):
🏁 Script executed:
#!/bin/bash
rg -nP 'VITE_EMAILJS_(SERVICE_ID|TEMPLATE_ID|PUBLIC_KEY|USER_ID)'Length of output: 285
Fix EmailJS env var and guard missing config
Replace the publicKey assignment to use VITE_EMAILJS_PUBLIC_KEY and bail out early if any of the three vars is unset to prevent silent failures:
src/pages/Contact/Contact.tsx
@@ -31,3 +31,9 @@
const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID;
- const publicKey = import.meta.env.VITE_EMAILJS_USER_ID;
+ const publicKey = import.meta.env.VITE_EMAILJS_PUBLIC_KEY;
+
+ if (!serviceId || !templateId || !publicKey) {
+ console.error("Missing EmailJS configuration (serviceId/templateId/publicKey).");
+ setIsSubmitting(false);
+ return;
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // EmailJS config (replace with your own) | |
| const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID; | |
| const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID | |
| const publicKey = import.meta.env.VITE_EMAILJS_USER_ID; | |
| // EmailJS config (replace with your own) | |
| const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID; | |
| const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID; | |
| const publicKey = import.meta.env.VITE_EMAILJS_PUBLIC_KEY; | |
| if (!serviceId || !templateId || !publicKey) { | |
| console.error("Missing EmailJS configuration (serviceId/templateId/publicKey)."); | |
| setIsSubmitting(false); | |
| return; | |
| } |
🤖 Prompt for AI Agents
In src/pages/Contact/Contact.tsx around lines 31 to 35, the publicKey is
assigned from the wrong env var and there is no guard for missing EmailJS
config; change the publicKey assignment to use
import.meta.env.VITE_EMAILJS_PUBLIC_KEY and add an early exit when any of
serviceId, templateId or publicKey is falsy (e.g., log an error and return from
the component or disable form submission) to prevent silent failures when env
vars are not set.
| try { | ||
| await emailjs.send(serviceId, templateId, templateParams, publicKey); | ||
| setShowPopup(true); | ||
| // Optionally clear fields | ||
| if (nameRef.current) nameRef.current.value = ""; | ||
| if (emailRef.current) emailRef.current.value = ""; | ||
| if (subjectRef.current) subjectRef.current.value = ""; | ||
| if (messageRef.current) messageRef.current.value = ""; | ||
| } catch (error) { | ||
| // Optionally handle error (show error popup, etc.) | ||
| } | ||
| setIsSubmitting(false); | ||
| // Auto-close popup after 5 seconds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Show user-facing errors and always reset submitting state in finally.
Errors are swallowed; users won’t know a send failed. Also prefer finally for resetting the loading flag.
Apply this diff:
- try {
- await emailjs.send(serviceId, templateId, templateParams, publicKey);
- setShowPopup(true);
- // Optionally clear fields
- if (nameRef.current) nameRef.current.value = "";
- if (emailRef.current) emailRef.current.value = "";
- if (subjectRef.current) subjectRef.current.value = "";
- if (messageRef.current) messageRef.current.value = "";
- } catch (error) {
- // Optionally handle error (show error popup, etc.)
- }
- setIsSubmitting(false);
+ try {
+ await emailjs.send(serviceId, templateId, templateParams, publicKey);
+ setShowPopup(true);
+ // Optionally clear fields
+ if (nameRef.current) nameRef.current.value = "";
+ if (emailRef.current) emailRef.current.value = "";
+ if (subjectRef.current) subjectRef.current.value = "";
+ if (messageRef.current) messageRef.current.value = "";
+ } catch (error) {
+ // Provide a user-visible notification
+ // (see import addition below)
+ toast.error("Failed to send your message. Please try again.");
+ console.error("EmailJS send failed:", error);
+ } finally {
+ setIsSubmitting(false);
+ }Add this import (top of file):
+import { toast } from "react-hot-toast";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| try { | |
| await emailjs.send(serviceId, templateId, templateParams, publicKey); | |
| setShowPopup(true); | |
| // Optionally clear fields | |
| if (nameRef.current) nameRef.current.value = ""; | |
| if (emailRef.current) emailRef.current.value = ""; | |
| if (subjectRef.current) subjectRef.current.value = ""; | |
| if (messageRef.current) messageRef.current.value = ""; | |
| } catch (error) { | |
| // Optionally handle error (show error popup, etc.) | |
| } | |
| setIsSubmitting(false); | |
| // Auto-close popup after 5 seconds | |
| // At the top of src/pages/Contact/Contact.tsx | |
| import { toast } from "react-hot-toast"; | |
| // … | |
| // In your submit handler, replace lines 43–55 with: | |
| try { | |
| await emailjs.send(serviceId, templateId, templateParams, publicKey); | |
| setShowPopup(true); | |
| // Optionally clear fields | |
| if (nameRef.current) nameRef.current.value = ""; | |
| if (emailRef.current) emailRef.current.value = ""; | |
| if (subjectRef.current) subjectRef.current.value = ""; | |
| if (messageRef.current) messageRef.current.value = ""; | |
| } catch (error) { | |
| // Provide a user-visible notification | |
| toast.error("Failed to send your message. Please try again."); | |
| console.error("EmailJS send failed:", error); | |
| } finally { | |
| setIsSubmitting(false); | |
| } | |
| // Auto-close popup after 5 seconds |
🤖 Prompt for AI Agents
In src/pages/Contact/Contact.tsx around lines 43 to 55, the send-email error is
currently swallowed and setIsSubmitting(false) is called outside a finally
block; update the try/catch to show a user-facing error (for example call
toast.error or set an error state and avoid showing the success popup) inside
the catch, and move setIsSubmitting(false) into a finally block so it always
runs; also add the required import at the top of the file (e.g., import { toast
} from 'react-toastify') and ensure the success popup is only shown on
successful send.
Related Issue
Description
This PR integrates EmailJS into the Contact page, enabling users to send messages directly from the contact form to the project team via email. The form fields are now connected to EmailJS, and upon submission, the data is sent using the configured EmailJS service, template, and public key. The UI and user experience remain unchanged, except for the new email functionality.
How Has This Been Tested?
Type of Change
EmailJS Setup Instructions
Create an EmailJS Account
Add an Email Service
Create an Email Template
from_name,from_email,subject,message).Get Your Service ID and Public Key
Configure Environment Variables
.envfile (or.env.localfor Vite/React).Update the Code to Use Environment Variables
import.meta.env.VITE_EMAILJS_SERVICE_ID, etc.Example:
Restart the Development Server
After updating the
.envfile, restart your dev server to apply the changes.Note:
Never commit your .env file to version control. Add .env to your .gitignore.
For more details, see the EmailJS documentation.
Summary by CodeRabbit