From 3a81f3008ab54afe1cf82ada8b3ceb5ccc4ec55f Mon Sep 17 00:00:00 2001 From: Ayush Date: Sun, 10 Nov 2024 10:39:11 +0530 Subject: [PATCH] added feedback form --- backend/controllers/complaintController.js | 79 +++++++++++- backend/models/feedbackmodal.js | 21 ++++ backend/routes/complaintRoutes.js | 12 +- frontend/package-lock.json | 66 +++++++--- frontend/package.json | 1 + frontend/src/App.jsx | 22 ++-- frontend/src/Pages/Feedback.jsx | 133 +++++++++++++++++++++ 7 files changed, 298 insertions(+), 36 deletions(-) create mode 100644 backend/models/feedbackmodal.js create mode 100644 frontend/src/Pages/Feedback.jsx diff --git a/backend/controllers/complaintController.js b/backend/controllers/complaintController.js index 55de0e0..b0478d6 100644 --- a/backend/controllers/complaintController.js +++ b/backend/controllers/complaintController.js @@ -1,11 +1,12 @@ import nodemailer from "nodemailer"; import Complaint from "../models/Complaint.js"; +import Feedback from "../models/feedbackmodal.js"; const transporter = nodemailer.createTransport({ service: "gmail", auth: { - user: process.env.EMAIL_USER, - pass: process.env.EMAIL_PASS, + user: "taskmaster991@gmail.com", + pass: "kmepakzcabvztekd", }, }); @@ -81,4 +82,76 @@ const submitComplaint = async (req, res) => { } }; -export default submitComplaint; +const sendFeedbackEmail = async (req, res) => { + const { senderEmail, message } = req.body; + + // Check if the email and message fields are provided + if (!senderEmail || !message) { + return res.status(400).json({ message: "Email and message are required." }); + } + + // Save the feedback data into the database + try { + const feedback = new Feedback({ + senderEmail, + message, + }); + + // Save feedback into the database + await feedback.save(); + + // Prepare the email content + const userMailOptions = { + from: process.env.EMAIL_USER, // Your email address from your environment variables + to: senderEmail, // The email address of the sender (the user who submitted feedback) + subject: "Thank you for your feedback on Station Saarthi", + html: ` +
+ +
+ Station Saarthi Logo +

Thank you for your feedback!

+
+ + +
+

Dear Customer,

+

We appreciate you taking the time to give us feedback. Here’s a copy of what you sent us:

+

Your Feedback: ${message}

+

If you have more thoughts, feel free to reach out to us again.

+
+ + +
+

Best Regards,
Station Saarthi Team

+

© ${new Date().getFullYear()} Station Saarthi. All rights reserved.

+
+
+ `, + attachments: [ + { + filename: "thank-you-image.jpg", + path: "C:/Users/ayush/OneDrive/Desktop/StationGuide/frontend/src/assets/station.png", + cid: "StationGuide", + }, + ], + }; + + // Send the email + await transporter.sendMail(userMailOptions); + console.log("Feedback confirmation email sent to the user."); + + // Send a success response to the client + res.status(200).json({ + message: "Feedback received successfully, confirmation email sent!", + }); + } catch (error) { + console.error("Error saving feedback or sending email:", error); + // Send error response if something goes wrong + res.status(500).json({ + message: "Error saving feedback or sending confirmation email.", + }); + } +}; + +export { submitComplaint, sendFeedbackEmail }; diff --git a/backend/models/feedbackmodal.js b/backend/models/feedbackmodal.js new file mode 100644 index 0000000..744b5c5 --- /dev/null +++ b/backend/models/feedbackmodal.js @@ -0,0 +1,21 @@ +import mongoose from "mongoose"; + +// Define the Feedback schema +const feedbackSchema = new mongoose.Schema( + { + senderEmail: { + type: String, + required: true, + match: [/\S+@\S+\.\S+/, "Please provide a valid email address"], + }, + message: { + type: String, + required: true, + maxlength: [500, "Feedback message cannot exceed 500 characters"], + }, + }, + { timestamps: true } +); + +const Feedback = mongoose.model("Feedback", feedbackSchema); +export default Feedback; diff --git a/backend/routes/complaintRoutes.js b/backend/routes/complaintRoutes.js index 9aef63b..406d398 100644 --- a/backend/routes/complaintRoutes.js +++ b/backend/routes/complaintRoutes.js @@ -1,11 +1,13 @@ // routes/complaint.js -import express from 'express' -import submitComplaint from '../controllers/complaintController.js'; - +import express from "express"; +import { submitComplaint } from "../controllers/complaintController.js"; +import { sendFeedbackEmail } from "../controllers/complaintController.js"; const router = express.Router(); // Handle complaint submission -router.post('/complaint', submitComplaint); +router.post("/complaint", submitComplaint); + +router.post("/userfeedback", sendFeedbackEmail); -export default router +export default router; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d28448b..d29afe1 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,6 +14,7 @@ "@mui/material": "^6.1.6", "@react-oauth/google": "^0.12.1", "antd": "^5.21.6", + "aos": "^2.3.4", "axios": "^1.7.7", "dotenv": "^16.4.5", "firebase": "^11.0.1", @@ -496,6 +497,15 @@ "node": ">=6.9.0" } }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz", @@ -515,6 +525,12 @@ "stylis": "4.2.0" } }, + "node_modules/@emotion/babel-plugin/node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, "node_modules/@emotion/babel-plugin/node_modules/@emotion/memoize": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", @@ -558,21 +574,6 @@ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", "license": "MIT" }, - "node_modules/@emotion/hash": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", - "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", - "license": "MIT" - }, - "node_modules/@ctrl/tinycolor": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", - "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/@emotion/hash": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", @@ -631,6 +632,12 @@ "csstype": "^3.0.2" } }, + "node_modules/@emotion/serialize/node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, "node_modules/@emotion/serialize/node_modules/@emotion/memoize": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", @@ -3429,6 +3436,17 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/aos": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/aos/-/aos-2.3.4.tgz", + "integrity": "sha512-zh/ahtR2yME4I51z8IttIt4lC1Nw0ktsFtmeDzID1m9naJnWXhCoARaCgNOGXb5CLy3zm+wqmRAEgMYB5E2HUw==", + "license": "MIT", + "dependencies": { + "classlist-polyfill": "^1.0.3", + "lodash.debounce": "^4.0.6", + "lodash.throttle": "^4.0.1" + } + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -3973,6 +3991,12 @@ "node": ">= 6" } }, + "node_modules/classlist-polyfill": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz", + "integrity": "sha512-GzIjNdcEtH4ieA2S8NmrSxv7DfEV5fmixQeyTmqmRmRJPGpRBaSnA2a0VrCjyT8iW8JjEdMbKzDotAJf+ajgaQ==", + "license": "Unlicense" + }, "node_modules/classnames": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", @@ -6546,6 +6570,12 @@ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "license": "MIT" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6553,6 +6583,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" + }, "node_modules/log-symbols": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 61c683c..8f76337 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "@mui/material": "^6.1.6", "@react-oauth/google": "^0.12.1", "antd": "^5.21.6", + "aos": "^2.3.4", "axios": "^1.7.7", "dotenv": "^16.4.5", "firebase": "^11.0.1", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 42c3425..f024177 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -29,13 +29,12 @@ import User from "./Pages/User"; // Added from the other version import ComplainBox from "./Pages/ComplainBox"; import Metadata from "./metadata"; import SettingsPage from "./Pages/Settings"; -import Faq from './Pages/Faq'; - +import Faq from "./Pages/Faq"; +import Feedback from "./Pages/feedback"; import TicketSearchComponent from "./Pages/TicketsAvailability"; import ProfilePage from "./Pages/Profile"; - function App() { return ( <> @@ -54,12 +53,9 @@ function App() { } /> } /> } /> - - } /> - + } /> + } /> } /> - - } /> } /> } /> @@ -70,7 +66,7 @@ function App() { } /> } /> } /> - } /> + } /> } />{" "} {/* Restored PrivacyPolicy */} } /> {/* Added User */} @@ -105,15 +101,15 @@ export function ProtectedRoute() { console.log("Token Verification error:", data.error); if (data.error || res.status === 400 || res.status === 500) { - navigate('/Login'); + navigate("/Login"); } if (res.status === 400 || res.status === 500) { - navigate('/Login'); + navigate("/Login"); } } catch (error) { - console.error('Error verifying token:', error); - navigate('/Login'); + console.error("Error verifying token:", error); + navigate("/Login"); } }; diff --git a/frontend/src/Pages/Feedback.jsx b/frontend/src/Pages/Feedback.jsx new file mode 100644 index 0000000..5aa2eb8 --- /dev/null +++ b/frontend/src/Pages/Feedback.jsx @@ -0,0 +1,133 @@ +import React, { useState } from "react"; +import { Navigate, useNavigate } from "react-router-dom"; + +const Feedback = () => { + const [formData, setFormData] = useState({ + senderEmail: "", + message: "", + }); + const [isLoading, setIsLoading] = useState(false); + const [alert, setAlert] = useState(false); + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData((prevFormData) => ({ + ...prevFormData, + [name]: value, + })); + }; + const Navigate = useNavigate(); + + const handleSubmit = async (e) => { + e.preventDefault(); + setIsLoading(true); + + try { + // Sending API request to submit the feedback + const response = await fetch("http://localhost:5000/api/userfeedback", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(formData), // Send form data as JSON + }); + + if (!response.ok) { + throw new Error("Failed to submit feedback"); + } + + setAlert(true); + + setFormData({ + senderEmail: "", + message: "", + }); + } catch (error) { + console.error("Error sending feedback:", error); + } finally { + setIsLoading(false); + setTimeout(() => setAlert(false), 3000); // Hide alert after 3 seconds + } + }; + + return ( +
+
+

+ Feedback Form +

+ +

Indian Railways

+ +
+
+

+ Submit Feedback +

+
+
+ + +
+ +
+ + +
+ + + {alert && ( +
+ Thank You, We will contact you soon. +
+ )} +
+
+
+ ); +}; + +export default Feedback;