fix update email

This commit is contained in:
2026-06-15 22:08:23 +05:30
parent 635bd4e7ba
commit ddbf352b33
5 changed files with 97 additions and 27 deletions

21
package-lock.json generated
View File

@@ -18,6 +18,7 @@
"lenis": "^1.3.23", "lenis": "^1.3.23",
"maath": "^0.10.8", "maath": "^0.10.8",
"next": "16.2.6", "next": "16.2.6",
"nodemailer": "^9.0.0",
"react": "19.2.4", "react": "19.2.4",
"react-dom": "19.2.4", "react-dom": "19.2.4",
"react-leaflet": "^5.0.0", "react-leaflet": "^5.0.0",
@@ -31,6 +32,7 @@
"@types/jest": "^30.0.0", "@types/jest": "^30.0.0",
"@types/leaflet": "^1.9.21", "@types/leaflet": "^1.9.21",
"@types/node": "^20", "@types/node": "^20",
"@types/nodemailer": "^8.0.1",
"@types/react": "^19", "@types/react": "^19",
"@types/react-dom": "^19", "@types/react-dom": "^19",
"@types/three": "^0.184.0", "@types/three": "^0.184.0",
@@ -3045,6 +3047,16 @@
"undici-types": "~6.21.0" "undici-types": "~6.21.0"
} }
}, },
"node_modules/@types/nodemailer": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-8.0.1.tgz",
"integrity": "sha512-PxpaInm8V1JQDd4j0ds5HfvWQk8JupS1C0Picb96QJsrrRDjBH+DlK7L4ZdNSqNULhiZRQHc40nLVShaGxXAMw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/offscreencanvas": { "node_modules/@types/offscreencanvas": {
"version": "2019.7.3", "version": "2019.7.3",
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz",
@@ -9018,6 +9030,15 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/nodemailer": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.0.tgz",
"integrity": "sha512-tbPTid7d/p9jAA8CRZ3iomvrMaST0o6NYuY7v6JQZHpPRZ61mLFSPKYd7342NtOFuej9/+L48SOIxwfu2uDvtw==",
"license": "MIT-0",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/normalize-path": { "node_modules/normalize-path": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",

View File

@@ -23,6 +23,7 @@
"lenis": "^1.3.23", "lenis": "^1.3.23",
"maath": "^0.10.8", "maath": "^0.10.8",
"next": "16.2.6", "next": "16.2.6",
"nodemailer": "^9.0.0",
"react": "19.2.4", "react": "19.2.4",
"react-dom": "19.2.4", "react-dom": "19.2.4",
"react-leaflet": "^5.0.0", "react-leaflet": "^5.0.0",
@@ -36,6 +37,7 @@
"@types/jest": "^30.0.0", "@types/jest": "^30.0.0",
"@types/leaflet": "^1.9.21", "@types/leaflet": "^1.9.21",
"@types/node": "^20", "@types/node": "^20",
"@types/nodemailer": "^8.0.1",
"@types/react": "^19", "@types/react": "^19",
"@types/react-dom": "^19", "@types/react-dom": "^19",
"@types/three": "^0.184.0", "@types/three": "^0.184.0",

53
src/actions/sendEmail.ts Normal file
View File

@@ -0,0 +1,53 @@
"use server";
import nodemailer from "nodemailer";
export async function sendContactEmail(data: {
fullName: string;
email: string;
subject: string;
message: string;
}) {
const { fullName, email, subject, message } = data;
if (!fullName || !email || !subject || !message) {
return { success: false, error: "All fields are required." };
}
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT) || 465,
secure: true,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASSWORD,
},
});
try {
await transporter.sendMail({
from: `"${fullName}" <${process.env.SMTP_USER}>`,
to: process.env.CONTACT_RECEIVER,
replyTo: email,
subject: `New Contact Form Submission: ${subject}`,
html: `
<div style="font-family: sans-serif; color: #333; max-width: 600px; margin: 0 auto; border: 1px solid #eee; padding: 20px; border-radius: 8px;">
<h2 style="color: #c01227; border-bottom: 2px solid #c01227; padding-bottom: 10px; margin-top: 0;">New Contact Form Submission</h2>
<p><strong>Name:</strong> ${fullName}</p>
<p><strong>Email:</strong> ${email}</p>
<p><strong>Subject:</strong> ${subject}</p>
<p><strong>Message:</strong></p>
<blockquote style="background: #f9f9f9; padding: 15px; border-left: 4px solid #c01227; margin: 0; font-style: italic;">
${message.replace(/\n/g, "<br />")}
</blockquote>
</div>
`,
});
return { success: true };
} catch (error) {
console.error("Email send error:", error);
const errorMessage = error instanceof Error ? error.message : "Failed to send the email. Please try again later.";
return { success: false, error: errorMessage };
}
}

View File

@@ -3,7 +3,7 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import emailjs from "@emailjs/browser"; import { sendContactEmail } from "@/actions/sendEmail";
import { ScrollReveal } from "@/animations/Reveal"; import { ScrollReveal } from "@/animations/Reveal";
export default function Footer() { export default function Footer() {
@@ -53,34 +53,24 @@ export default function Footer() {
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
// EmailJS is fully client-side — only the public-safe credentials are used
// (Service ID, Template ID, Public Key). No private/secret key, no backend route.
const serviceId = process.env.NEXT_PUBLIC_EMAILJS_SERVICE_ID;
const templateId = process.env.NEXT_PUBLIC_EMAILJS_TEMPLATE_ID;
const publicKey = process.env.NEXT_PUBLIC_EMAILJS_PUBLIC_KEY;
if (!serviceId || !templateId || !publicKey) {
console.error("EmailJS env vars are missing — set NEXT_PUBLIC_EMAILJS_* in .env.local");
setFormStatus("error");
return;
}
setFormStatus("submitting"); setFormStatus("submitting");
try { try {
await emailjs.send( const res = await sendContactEmail({
serviceId, fullName: formData.fullName,
templateId,
{
name: formData.fullName,
email: formData.email, email: formData.email,
subject: formData.subject, subject: formData.subject,
message: formData.message, message: formData.message,
}, });
publicKey,
); if (res.success) {
setFormStatus("success"); setFormStatus("success");
setFormData({ fullName: "", email: "", subject: "", message: "" }); setFormData({ fullName: "", email: "", subject: "", message: "" });
} else {
console.error("Failed to send contact email:", res.error);
setFormStatus("error");
}
} catch (err) { } catch (err) {
console.error(err); console.error("Error submitting contact form:", err);
setFormStatus("error"); setFormStatus("error");
} }
}; };

View File

@@ -827,7 +827,11 @@ function SceneReadySignal({ onReady }: { onReady?: () => void }) {
}; };
try { try {
const compile = renderer.compileAsync const hasParallelExtension =
typeof renderer.getContext === "function" &&
!!renderer.getContext()?.getExtension?.("KHR_parallel_shader_compile");
const compile = (typeof renderer.compileAsync === "function" && hasParallelExtension)
? renderer.compileAsync(scene, camera) ? renderer.compileAsync(scene, camera)
: Promise.resolve().then(() => renderer.compile(scene, camera)); : Promise.resolve().then(() => renderer.compile(scene, camera));