diff --git a/backend/controller/reviewController.js b/backend/controller/reviewController.js index 3f2f317..af9bbfd 100644 --- a/backend/controller/reviewController.js +++ b/backend/controller/reviewController.js @@ -4,6 +4,10 @@ import Product from "../model/productModel.js"; import User from "../model/userModel.js"; import { sendNotification } from "../services/notificationService.js"; +// 1. Import and initialize the sentiment library +import Sentiment from 'sentiment'; +const sentiment = new Sentiment(); + async function recalculateProductRating(productId) { const result = await Review.aggregate([ { $match: { productId: new mongoose.Types.ObjectId(productId) } }, @@ -45,6 +49,19 @@ export const addReview = async (req, res) => { return res.status(404).json({ message: "Product not found" }); } + // 2. Perform the sentiment analysis on the comment text + let sentimentScore = 0; + let sentimentLabel = 'Neutral'; + + if (comment && comment.trim().length > 0) { + const result = sentiment.analyze(comment); + sentimentScore = result.score; + + // Assign labels based on the score threshold + if (sentimentScore >= 2) sentimentLabel = 'Positive'; + else if (sentimentScore <= -2) sentimentLabel = 'Negative'; + } + const existingReview = await Review.findOne({ userId: req.userId, productId, @@ -53,6 +70,10 @@ export const addReview = async (req, res) => { if (existingReview) { existingReview.rating = rating; existingReview.comment = comment; + // 3. Update existing review with new sentiment data + existingReview.sentimentScore = sentimentScore; + existingReview.sentimentLabel = sentimentLabel; + await existingReview.save(); const { avgRating, reviewCount } = await recalculateProductRating(productId); @@ -65,12 +86,15 @@ export const addReview = async (req, res) => { }); } + // 4. Create new review with sentiment data attached const newReview = new Review({ userId: req.userId, productId, name: user.name, rating, comment, + sentimentScore, + sentimentLabel }); await newReview.save(); @@ -115,4 +139,4 @@ export const getProductReviews = async (req, res) => { console.log(error); res.status(500).json({ message: "Server error" }); } -}; +}; \ No newline at end of file diff --git a/backend/model/reviewModel.js b/backend/model/reviewModel.js index b835550..0120c0a 100644 --- a/backend/model/reviewModel.js +++ b/backend/model/reviewModel.js @@ -30,6 +30,18 @@ const reviewSchema = new mongoose.Schema( type: String, required: true, }, + // Add this inside the reviewSchema definition +sentimentScore: { + type: Number, + required: false, + default: 0 // Scores usually range from negative to positive numbers +}, +sentimentLabel: { + type: String, + enum: ['Positive', 'Neutral', 'Negative'], + default: 'Neutral' +} + }, { timestamps: true, diff --git a/frontend/src/pages/ProductDetail.jsx b/frontend/src/pages/ProductDetail.jsx index 72d4218..47bd50c 100644 --- a/frontend/src/pages/ProductDetail.jsx +++ b/frontend/src/pages/ProductDetail.jsx @@ -8,7 +8,6 @@ import 'react-toastify/dist/ReactToastify.css'; import { FaChevronLeft, FaChevronRight, FaHeart, FaShare, FaShoppingCart, FaStar } from 'react-icons/fa'; import RelatedProduct from '../components/RelatedProduct'; - function ProductDetail() { const { userData } = useContext(userDataContext); const { productId } = useParams(); @@ -120,16 +119,17 @@ function ProductDetail() { }; const handleAddToWishlist = () => { - if (!productData?._id) return; - - if (isWishlisted) { - removeFromWishlist(productData._id); - toast.info('Removed from wishlist'); - } else { - addToWishlist(productData._id); - toast.success('Added to wishlist 💖'); - } -}; + if (!productData?._id) return; + + if (isWishlisted) { + removeFromWishlist(productData._id); + toast.info('Removed from wishlist'); + } else { + addToWishlist(productData._id); + toast.success('Added to wishlist 💖'); + } + }; + // share const handleShare = async () => { if (navigator.share) { @@ -460,16 +460,16 @@ function ProductDetail() {
+ onClick={handleAddToWishlist} + className={`flex-1 py-3 rounded-lg flex items-center justify-center gap-2 transition-all duration-200 + ${isWishlisted + ? 'bg-rose-600 text-white' + : 'bg-gray-800 text-white hover:bg-gray-700' + }`} + > + + {isWishlisted ? 'Wishlisted' : 'Wishlist'} +
- - - {review.name} - +
+ + {review.name} + + + {/* Integrated Sentiment Badge for the General Review List */} + {review.sentimentLabel === 'Positive' && ( + + Helpful Positive + + )} + {review.sentimentLabel === 'Negative' && ( + + Critical Review + + )} + +

{review.comment} @@ -697,4 +725,4 @@ function ProductDetail() { ); } -export default ProductDetail; +export default ProductDetail; \ No newline at end of file