Skip to content
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

Newsletter saved to db and a thank you mail is sent to email #174

Merged
merged 4 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,15 +213,22 @@ Special thanks to our amazing mentors who are guiding this project! 🙌
<sub><b>Jai Dhingra</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Jay-1409">
<img src="https://avatars.githubusercontent.com/u/166749819?v=4" width="100;" alt="Jay-1409"/>
<br />
<sub><b>Jay shah</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/Mohitranag18">
<img src="https://avatars.githubusercontent.com/u/152625405?v=4" width="100;" alt="Mohitranag18"/>
<br />
<sub><b>Mohit Rana </b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/MutiatBash">
<img src="https://avatars.githubusercontent.com/u/108807732?v=4" width="100;" alt="MutiatBash"/>
Expand Down
34 changes: 34 additions & 0 deletions backend/config/nodemailer.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,40 @@ const transporter = nodemailer.createTransport({
},
});

// Function to send newsletter subscription confirmation via email
exports.sendSubscriptionConfirmation = async (email) => {
// Construct the email content
const emailText = `
Dear Customer,

Thank you for subscribing to our newsletter! We're excited to have you on board.

You will now receive regular updates about our latest boardgame collections, special offers, and upcoming events.

If you have any questions or feedback, feel free to reach out.

Best regards,
PlayCafe Team
`;
Comment on lines +17 to +28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using HTML formatting for the email content

The email content is well-structured and informative. However, plain text emails may not be as visually appealing or engaging for users.

Consider using HTML formatting for the email content to improve readability and allow for more attractive styling. Here's an example of how you could modify the code:

-  const emailText = `
-    Dear Customer,
-    
-    Thank you for subscribing to our newsletter! We're excited to have you on board.
-
-    You will now receive regular updates about our latest boardgame collections, special offers, and upcoming events.
-
-    If you have any questions or feedback, feel free to reach out.
-
-    Best regards,
-    PlayCafe Team
-  `;
+  const emailHtml = `
+    <html>
+      <body>
+        <h2>Dear Customer,</h2>
+        <p>Thank you for subscribing to our newsletter! We're excited to have you on board.</p>
+        <p>You will now receive regular updates about our latest boardgame collections, special offers, and upcoming events.</p>
+        <p>If you have any questions or feedback, feel free to reach out.</p>
+        <p>Best regards,<br>PlayCafe Team</p>
+      </body>
+    </html>
+  `;

   // In the sendMail options:
-  text: emailText,
+  html: emailHtml,

This change would allow for better formatting and potentially include images or styled elements in the future.

📝 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.

Suggested change
const emailText = `
Dear Customer,
Thank you for subscribing to our newsletter! We're excited to have you on board.
You will now receive regular updates about our latest boardgame collections, special offers, and upcoming events.
If you have any questions or feedback, feel free to reach out.
Best regards,
PlayCafe Team
`;
const emailHtml = `
<html>
<body>
<h2>Dear Customer,</h2>
<p>Thank you for subscribing to our newsletter! We're excited to have you on board.</p>
<p>You will now receive regular updates about our latest boardgame collections, special offers, and upcoming events.</p>
<p>If you have any questions or feedback, feel free to reach out.</p>
<p>Best regards,<br>PlayCafe Team</p>
</body>
</html>
`;


try {
await transporter.sendMail({
from: process.env.EMAIL_USER,
to: email,
subject: "Thank You for Subscribing!",
text: emailText,
});
logger.info('Newsletter subscription confirmation sent successfully via email', { email });
} catch (error) {
logger.error('Failed to send newsletter subscription confirmation email', { error, email });
if (error.code === 'ECONNREFUSED') {
throw new Error('Failed to connect to email server. Please try again later.');
} else {
throw new Error(`Failed to send subscription confirmation email: ${error.message}`);
}
Comment on lines +37 to +44
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance error handling and logging

The current error handling is good, but there's room for improvement in terms of providing more context and potentially handling different types of errors.

Consider the following enhancements:

  1. Log the full error object for debugging purposes.
  2. Handle more specific error types, such as authentication errors.
  3. Include the attempted email address in the error message for easier troubleshooting.

Here's an example of how you could modify the code:

-    logger.error('Failed to send newsletter subscription confirmation email', { error, email });
+    logger.error('Failed to send newsletter subscription confirmation email', { error: error.toString(), stack: error.stack, email });
     if (error.code === 'ECONNREFUSED') {
-      throw new Error('Failed to connect to email server. Please try again later.');
+      throw new Error(`Failed to connect to email server for ${email}. Please try again later.`);
+    } else if (error.code === 'EAUTH') {
+      throw new Error(`Authentication failed when sending email to ${email}. Please check email service credentials.`);
     } else {
-      throw new Error(`Failed to send subscription confirmation email: ${error.message}`);
+      throw new Error(`Failed to send subscription confirmation email to ${email}: ${error.message}`);
     }

These changes provide more context in the logs and error messages, which can be crucial for debugging and monitoring the application's email functionality.

📝 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.

Suggested change
logger.info('Newsletter subscription confirmation sent successfully via email', { email });
} catch (error) {
logger.error('Failed to send newsletter subscription confirmation email', { error, email });
if (error.code === 'ECONNREFUSED') {
throw new Error('Failed to connect to email server. Please try again later.');
} else {
throw new Error(`Failed to send subscription confirmation email: ${error.message}`);
}
logger.info('Newsletter subscription confirmation sent successfully via email', { email });
} catch (error) {
logger.error('Failed to send newsletter subscription confirmation email', { error: error.toString(), stack: error.stack, email });
if (error.code === 'ECONNREFUSED') {
throw new Error(`Failed to connect to email server for ${email}. Please try again later.`);
} else if (error.code === 'EAUTH') {
throw new Error(`Authentication failed when sending email to ${email}. Please check email service credentials.`);
} else {
throw new Error(`Failed to send subscription confirmation email to ${email}: ${error.message}`);
}

}
};

// Function to send reservation confirmation via email
exports.sendReservationConfirmation = async (email, reservationDetails) => {
const { reservationDate, guests, time } = reservationDetails;
Expand Down
35 changes: 35 additions & 0 deletions backend/controller/newsletter.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const NewsletterEmail = require('../models/newsletter.model'); // Import the Mongoose model
const { sendSubscriptionConfirmation } = require('../config/nodemailer'); // Import the mailer function

// Controller for handling newsletter subscriptions
exports.subscribeToNewsletter = async (req, res) => {
const { email } = req.body;

if (!email) {
return res.status(400).json({ error: 'Email is required' });
}
Comment on lines +8 to +10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding email format validation.

While the code checks if an email is provided, it doesn't validate the email format. Consider adding a regex check or using a validation library to ensure the email is in a valid format before proceeding with the subscription process.

Here's a simple regex check you could add:

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email || !emailRegex.test(email)) {
    return res.status(400).json({ error: 'Please provide a valid email address' });
}


try {
// Check if the email already exists in the database
const existingEmail = await NewsletterEmail.findOne({ email });
if (existingEmail) {
return res.status(400).json({ error: 'This email is already subscribed.' });
}

// Save the email to the database
const newEmail = new NewsletterEmail({ email });
await newEmail.save();

try {
await sendSubscriptionConfirmation(email);
} catch (error) {
console.error('Error sending confirmation email:', error);
return res.status(500).json({ error: 'Subscription successful, but there was an error sending the confirmation email.' });
}

return res.status(201).json({ message: 'Subscription successful! A confirmation email has been sent.' });
} catch (error) {
console.error('Error subscribing to newsletter:', error);
return res.status(500).json({ error: 'Error subscribing to the newsletter.' });
}
Comment on lines +31 to +34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve error handling with more specific error messages.

While the current error handling catches and logs errors, it returns a generic message for all types of errors. This might make it difficult to debug issues in production.

Consider categorizing errors and returning more specific messages:

} catch (error) {
    console.error('Error subscribing to newsletter:', error);
    if (error.name === 'ValidationError') {
        return res.status(400).json({ error: 'Invalid email format.' });
    } else if (error.code === 11000) {
        return res.status(400).json({ error: 'This email is already subscribed.' });
    } else {
        return res.status(500).json({ error: 'An unexpected error occurred. Please try again later.' });
    }
}

This will provide more helpful feedback to both users and developers.

};
19 changes: 19 additions & 0 deletions backend/models/newsletter.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const mongoose = require('mongoose');

// Define the schema for newsletter emails
const NewsletterEmailSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true, // Ensure no duplicate emails
trim: true,
match: [/.+\@.+\..+/, 'Please enter a valid email address'], // Simple email validation
},
subscribedAt: {
type: Date,
default: Date.now, // Automatically set the date of subscription
}
});
Comment on lines +4 to +16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider enhancing email validation.

The schema structure looks good overall. However, the email validation regex (/.+\@.+\..+/) is quite basic and may allow some invalid email formats. Consider using a more robust regex pattern for email validation to ensure better data integrity.

Here's a suggested improvement for the email validation:

 email: {
     type: String,
     required: true,
     unique: true,
     trim: true,
-    match: [/.+\@.+\..+/, 'Please enter a valid email address'],
+    match: [/^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/, 'Please enter a valid email address'],
 },

This regex pattern provides a more comprehensive check for email format validity.

📝 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.

Suggested change
const NewsletterEmailSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true, // Ensure no duplicate emails
trim: true,
match: [/.+\@.+\..+/, 'Please enter a valid email address'], // Simple email validation
},
subscribedAt: {
type: Date,
default: Date.now, // Automatically set the date of subscription
}
});
const NewsletterEmailSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true, // Ensure no duplicate emails
trim: true,
match: [/^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/, 'Please enter a valid email address'], // More comprehensive email validation
},
subscribedAt: {
type: Date,
default: Date.now, // Automatically set the date of subscription
}
});


// Export the model
module.exports = mongoose.model('NewsletterEmail', NewsletterEmailSchema);
1 change: 1 addition & 0 deletions backend/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ router.use("/admin", require("./adminRouter"));
router.use("/feedback", feedbackRouter);
router.use("/user", require("./customerRouter"));
router.use("/reservation", require("./reservationRouter"));
router.use("/newsletter", require("./newsletterRoute"));


module.exports = router;
10 changes: 10 additions & 0 deletions backend/routes/newsletterRoute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const express = require("express");
const { subscribeToNewsletter } = require("../controller/newsletter.controller"); // Import the controller
const router = express.Router();
require("dotenv").config();



router.post("/subscribe", subscribeToNewsletter);

module.exports = router;
52 changes: 52 additions & 0 deletions frontend/src/components/Pages/Boardgame.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,37 @@ import board8 from "../../assets/Boardgames/board8.png";
import board10 from "../../assets/Boardgames/board10.png";
import bg from "../../assets/Boardgames/bg.jpg";


import { LazyLoadImage } from 'react-lazy-load-image-component';
import 'react-lazy-load-image-component/src/effects/blur.css';

export default function Boardgame() {
const [selectedBoard, setSelectedBoard] = useState(null);
const [email, setEmail] = useState('');

const handleSubmit = async (e) => {
e.preventDefault();
try {
// Make the POST request to /newsletter/subscribe endpoint using fetch
const response = await fetch(`${import.meta.env.VITE_BACKEND_URL}/api/newsletter/subscribe`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
});

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

const data = await response.json();
alert('Subscription successful! Check your email for confirmation.');
} catch (error) {
console.error('Error subscribing to newsletter:', error);
alert('Error subscribing. Please try again.');
}
};

const handleOpenInstructions = (board) => {
setSelectedBoard(board);
Expand Down Expand Up @@ -217,6 +243,32 @@ export default function Boardgame() {
))}
</div>
</section>
<section className="w-full py-12 md:py-24 lg:py-32 bg-gray-100">
<div className="container mx-auto px-4 md:px-6 text-center">
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl lg:text-6xl mb-4">
Subscribe to our Newsletter
</h2>
<p className="mx-auto max-w-[700px] text-muted-foreground md:text-xl mb-6">
Stay updated with our latest boardgame collections, special offers, and events. Subscribe now and never miss out!
</p>
<form className="flex flex-col items-center space-y-4 sm:space-y-0 sm:flex-row sm:space-x-4 justify-center" onSubmit={handleSubmit}>
<input
type="email"
className="px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Enter your email"
value={email}
onChange={(e) => setEmail(e.target.value)} // Update email state on input change
required
/>
<button
type="submit"
className="px-6 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
>
Subscribe
</button>
</form>
</div>
</section>

{/* Modal for instructions */}
{selectedBoard && (
Expand Down
Loading