diff --git a/api/index.js b/api/index.js index a137262..a756983 100644 --- a/api/index.js +++ b/api/index.js @@ -4,6 +4,7 @@ const cors = require('cors'); const mongo = require('mongodb'); const mongoose = require('mongoose'); const mongoSanitize = require('express-mongo-sanitize'); +const nodemailer = require('nodemailer'); const app = express() app.use(cors()) @@ -216,21 +217,56 @@ app.get('/api/users', async (req, res) => { // Post Contact app.post('/api/contact', async (req, res) => { const { name, email, subject, message } = req.body + try { const newContact = new Contact({ name, email, subject, message - }) - await newContact.save() + }); + await newContact.save(); + + // Nodemailer setup + const transporter = nodemailer.createTransport({ + service: 'gmail', + auth: { + user: process.env.ADMIN_EMAIL, + pass: process.env.ADMIN_PASSWORD, + }, + }); + + transporter.verify((error, success) => { + if (error) { + console.error('Error initializing transporter:', error); + } else { + console.log('Transporter is ready to send emails', success); + } + }); + + + const mailOptions = { + from: email, + to: process.env.ADMIN_EMAIL, + subject: `Contact Form: ${subject}`, + html: ` +
From: ${name} (${email})
+Subject: ${subject}
+Message:
+${message}
+ ` + }; - res.status(201).json({ message: 'Inquiry submitted successfully' }) + // Send email + await transporter.sendMail(mailOptions); + + res.status(201).json({ message: 'Inquiry and email submitted successfully' }); } catch (err) { - res.status(500).json({ message: 'Error submitting inquiry' }); + console.error('Error submitting inquiry:', err); + res.status(500).json({ message: 'Error submitting inquiry', error: err.message }); } -}) +}); /* CLASS RELATED ENDPOINTS */ diff --git a/package-lock.json b/package-lock.json index a56bb88..a421168 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "i18next": "^23.16.4", "i18next-browser-languagedetector": "^8.0.0", "i18next-http-backend": "^2.6.2", + "nodemailer": "^6.9.16", "react": "^18.3.1", "react-dom": "^18.3.1", "react-i18next": "^15.1.0", @@ -2785,9 +2786,9 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -2808,7 +2809,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -2823,6 +2824,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express-mongo-sanitize": { @@ -4261,9 +4266,9 @@ } }, "node_modules/mongoose": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.2.tgz", - "integrity": "sha512-jCTSqDANfRzk909v4YoZQi7jlGRB2MTvgG+spVBc/BA4tOs1oWJr//V6yYujqNq9UybpOtsSfBqxI0dSOEFJHQ==", + "version": "8.8.4", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.4.tgz", + "integrity": "sha512-yJbn695qCsqDO+xyPII29x2R7flzXhxCDv09mMZPSGllf0sm4jKw3E9s9uvQ9hjO6bL2xjU8KKowYqcY9eSTMQ==", "dependencies": { "bson": "^6.7.0", "kareem": "2.6.3", @@ -4418,6 +4423,15 @@ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, + "node_modules/nodemailer": { + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz", + "integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nodemon": { "version": "3.1.7", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", @@ -4732,9 +4746,9 @@ "dev": true }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, "node_modules/picocolors": { "version": "1.1.1", diff --git a/package.json b/package.json index 0f412a9..476cdc3 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "i18next": "^23.16.4", "i18next-browser-languagedetector": "^8.0.0", "i18next-http-backend": "^2.6.2", + "nodemailer": "^6.9.16", "react": "^18.3.1", "react-dom": "^18.3.1", "react-i18next": "^15.1.0", diff --git a/src/api/contact-wrapper.js b/src/api/contact-wrapper.js index 35afc2b..cebb159 100644 --- a/src/api/contact-wrapper.js +++ b/src/api/contact-wrapper.js @@ -7,14 +7,22 @@ const postContact = async (body) => { headers: { "Content-Type": "application/json", }, - body: JSON.stringify(body) - }) + body: JSON.stringify(body), + }); - return response + if (!response.ok) { + const errorResponse = await response.json(); + console.error('Error response from /api/contact:', errorResponse); + throw new Error(errorResponse.message || 'Failed to submit contact form'); + } + + return response; } catch (error) { console.error('Contact endpoint post error:', error); + throw error; // Propagate the error to the caller } -} +}; + export { postContact diff --git a/src/pages/Contact.jsx b/src/pages/Contact.jsx index d37ff7a..0c3740a 100644 --- a/src/pages/Contact.jsx +++ b/src/pages/Contact.jsx @@ -4,8 +4,10 @@ import { postContact } from '@/api/contact-wrapper'; import Form from "@/components/Form/Form" import FormInput from '@/components/Form/FormInput'; import FormSubmit from '@/components/Form/FormSubmit'; +import { useTranslation } from 'react-i18next'; export default function Contact() { + const { t } = useTranslation(); const [formData, setFormData] = useState({ name: '', @@ -20,19 +22,24 @@ export default function Contact() { const handleSubmit = async (e) => { e.preventDefault(); + const contactData = { + name: e.target.name.value, + email: e.target.email.value, + subject: e.target.subject.value, + message: e.target.message.value + }; try { - const response = await postContact(formData) + const response = await postContact(contactData); - if (!response.ok) { - throw new Error(`Response status: ${response.status}`); + if (response.ok) { + alert("Message submitted successfully!"); + } else { + const errorResponse = await response.json(); + alert(`Failed to send message: ${errorResponse.message}`); } - const json = await response.json() - console.log(json) - alert("Inquiry submitted successfully!") - } catch (err) { - console.error(error.message) - alert("There was an error submitting the inquiry.") + console.error('Error in handleSubmit:', err); + alert("There was an error submitting the inquiry."); } }; @@ -40,9 +47,9 @@ export default function Contact() { return (- Email: + {t("email_field")}: dillarenglish@gmail.com @@ -58,9 +65,9 @@ export default function Contact() {