Skip to content

Commit

Permalink
synced fork
Browse files Browse the repository at this point in the history
  • Loading branch information
samar12-rad committed Oct 9, 2024
2 parents 36ceef6 + 0859569 commit c559c47
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 41 deletions.
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
`;

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}`);
}
}
};

// 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' });
}

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.' });
}
};
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
}
});

// Export the model
module.exports = mongoose.model('NewsletterEmail', NewsletterEmailSchema);
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.21.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.7.0",
"nodemailer": "^6.9.15",
"validator": "^13.12.0",
Expand Down
1 change: 1 addition & 0 deletions backend/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,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
13 changes: 8 additions & 5 deletions frontend/src/components/Pages/Login.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Link } from "react-router-dom";
import { Link, useNavigate } from "react-router-dom";
import photo from "../../assets/login.png";
import React, { useState } from "react";
import { message } from "antd";

const Login = () => {
const API_URL = import.meta.env.VITE_BACKEND_URL || "http://localhost:3000";
Expand All @@ -12,7 +13,7 @@ const Login = () => {
const handleChange = (e) => {
setData({ ...data, [e.target.name]: e.target.value });
};

const navigate = useNavigate(); // Correctly initialize useNavigate
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);

Expand All @@ -32,10 +33,11 @@ const Login = () => {
if (!response.ok) {
throw new Error(result.message || "Login failed");
}
console.log(result);
// Handle successful login (e.g., store token, redirect)
message.success("Login successful");
navigate("/");
} catch (err) {
setError(err.message);
setError(err.message || "An error occurred. Please try again.");
} finally {
setIsLoading(false);
}
Expand Down Expand Up @@ -77,11 +79,12 @@ const Login = () => {
<Link to={"/signup"}>Register Here</Link>
</span>
</h3>
{error && <p className="text-red-500 mt-2">{error}</p>}
<button
type="submit"
className="button-confirm mx-auto mt-12 px-4 w-30 h-10 rounded-md border-2 border-black bg-beige shadow-[4px_4px_0px_0px_black] text-[17px] font-semibold text-[#323232] cursor-pointer active:shadow-none active:translate-x-[3px] active:translate-y-[3px]"
>
Let’s go →
{isLoading ? "Loading..." : "Let’s Log you in →"}
</button>
</form>
</div>
Expand Down
22 changes: 21 additions & 1 deletion frontend/src/components/Pages/ResetPassword.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Link, useNavigate } from "react-router-dom";
import photo from "../../assets/login.png";
import React, { useState } from "react";
import { message } from "antd";

const ResetPassword = () => {
const API_URL = import.meta.env.VITE_BACKEND_URL || "http://localhost:3000";
Expand All @@ -18,11 +19,28 @@ const ResetPassword = () => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);

// Helper function for email validation
const isValidEmail = (email) => {
// Basic email regex, consider using a more robust solution in production
return /\S+@\S+\.\S+/.test(email);
};

const handleSubmit = async (e) => {
e.preventDefault();
setIsLoading(true);
setError(null);

// Add input validation // Basic validation examples
if (!isValidEmail(data.email)) {
setError("Please enter a valid email address");
return;
}

if (data.password.length < 8) {
setError("Password must be at least 8 characters long");
return;
}

const passwordMatch = data.password === data.confirmPassword;
if (!passwordMatch) {
setError("Passwords do not match");
Expand All @@ -44,7 +62,7 @@ const ResetPassword = () => {
}

// Display success message and navigate to login
alert("Password reset successfully! Please log in.");
message.success("Password reset successfully! Please log in.");
navigate("/login");
} catch (err) {
setError(err.message);
Expand Down Expand Up @@ -79,6 +97,8 @@ const ResetPassword = () => {
name="email"
placeholder="Email"
type="email"
aria-required="true"
autoComplete="email"
onChange={(e) => handleChange(e)}
/>

Expand Down
Loading

0 comments on commit c559c47

Please sign in to comment.