From 425650af30197a21ec668947647aa4795f6c6ca3 Mon Sep 17 00:00:00 2001 From: haseebzaki-07 Date: Sun, 10 Nov 2024 12:52:26 +0530 Subject: [PATCH] add ratings/reviews --- frontend/package-lock.json | 8 +- frontend/package.json | 2 +- .../src/AgroRentAI/RentProductDetails.jsx | 5 +- .../components/RatingProductReview.jsx | 252 ++++++++++++++++++ 4 files changed, 261 insertions(+), 6 deletions(-) create mode 100644 frontend/src/AgroRentAI/components/RatingProductReview.jsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0b298baf..ef1b3531 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -26,7 +26,7 @@ "@react-three/fiber": "^8.17.10", "axios": "^1.7.7", "bootstrap": "^5.3.3", - "chart.js": "^4.4.3", + "chart.js": "^4.4.6", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "date-fns": "^4.1.0", @@ -3570,9 +3570,9 @@ } }, "node_modules/chart.js": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.4.tgz", - "integrity": "sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==", + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.6.tgz", + "integrity": "sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==", "dependencies": { "@kurkle/color": "^0.3.0" }, diff --git a/frontend/package.json b/frontend/package.json index 82fba39a..a4d00ba7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,7 +28,7 @@ "@react-three/fiber": "^8.17.10", "axios": "^1.7.7", "bootstrap": "^5.3.3", - "chart.js": "^4.4.3", + "chart.js": "^4.4.6", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "date-fns": "^4.1.0", diff --git a/frontend/src/AgroRentAI/RentProductDetails.jsx b/frontend/src/AgroRentAI/RentProductDetails.jsx index 5613e994..1dbdf08b 100644 --- a/frontend/src/AgroRentAI/RentProductDetails.jsx +++ b/frontend/src/AgroRentAI/RentProductDetails.jsx @@ -3,6 +3,8 @@ import { Star } from 'lucide-react'; import { useParams } from 'react-router-dom'; import { toast, ToastContainer } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; +import ProductReview from './components/RatingProductReview'; + const RentProductDetails = () => { const [product, setProduct] = useState(null); @@ -84,7 +86,7 @@ const RentProductDetails = () => { } return ( -
+
{/* Heading */}

Product Details

@@ -150,6 +152,7 @@ const RentProductDetails = () => {
+ ); }; diff --git a/frontend/src/AgroRentAI/components/RatingProductReview.jsx b/frontend/src/AgroRentAI/components/RatingProductReview.jsx new file mode 100644 index 00000000..cc49c759 --- /dev/null +++ b/frontend/src/AgroRentAI/components/RatingProductReview.jsx @@ -0,0 +1,252 @@ +import React, { useState } from 'react'; +import { Star } from 'lucide-react'; +import { toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +import { Bar } from 'react-chartjs-2'; +import { FaThumbsUp, FaThumbsDown } from 'react-icons/fa'; + +import { + Chart as ChartJS, + BarElement, + CategoryScale, + LinearScale, + Tooltip, + Legend, +} from 'chart.js'; + +ChartJS.register(BarElement, CategoryScale, LinearScale, Tooltip, Legend); + +const ProductReview = () => { + const [newReview, setNewReview] = useState({ rating: 0, title: '', comment: '' }); + const [averageRating, setAverageRating] = useState(4.0); + const [ratingDistribution, setRatingDistribution] = useState([1, 1, 1, 1, 1]); + const [helpfulReview, setHelpfulReview] = useState({}); + const [filterRating, setFilterRating] = useState(0); + const [sortOrder, setSortOrder] = useState('newest'); + + const [reviews, setReviews] = useState([ + { + _id: '1', + rating: 5, + title: 'Amazing Product!', + comment: 'This product exceeded my expectations. Highly recommend!', + date: '2023-10-01', + }, + { + _id: '2', + rating: 4, + title: 'Very Good', + comment: 'The product is really good, though it could use a few improvements.', + date: '2023-09-15', + }, + { + _id: '3', + rating: 3, + title: 'Average', + comment: 'It’s okay, does the job but nothing extraordinary.', + date: '2023-08-20', + }, + ]); + + const handleReviewSubmit = () => { + if (newReview.rating === 0 || !newReview.comment) { + return toast.error("Please provide a rating and a comment."); + } + const updatedReviews = [...reviews, { ...newReview, _id: Date.now().toString(), date: new Date().toISOString() }]; + setReviews(updatedReviews); + toast.success("Review submitted successfully!"); + setNewReview({ rating: 0, title: '', comment: '' }); + }; + + // Fetch reviews and rating distribution +// useEffect(() => { + // const fetchReviews = async () => { + // try { + // const response = await fetch(`${apiUrl}/api/rent-products/${productId}/reviews`); + // const data = await response.json(); + // setReviews(data.reviews); + // setAverageRating(data.averageRating); + // setRatingDistribution(data.ratingDistribution); + // } catch (error) { + // console.error('Error fetching reviews:', error); + // } + // }; + // fetchReviews(); +// }, [productId, apiUrl]); + + // Handle new review submission +// const handleReviewSubmit = async () => { +// if (newReview.rating === 0 || !newReview.comment) { +// return toast.error("Please provide a rating and a comment."); +// } + +// try { +// const response = await fetch(`${apiUrl}/api/rent-products/${productId}/reviews`, { +// method: 'POST', +// headers: { 'Content-Type': 'application/json' }, +// body: JSON.stringify(newReview), +// }); + +// if (response.ok) { +// toast.success("Review submitted successfully!"); +// setNewReview({ rating: 0, title: '', comment: '' }); +// } else { +// const errorData = await response.json(); +// toast.error(`Error: ${errorData.message}`); +// } +// } catch (error) { +// toast.error('Failed to submit review'); +// } +// }; + + const markReviewHelpful = (reviewId, isHelpful) => { + setHelpfulReview((prevHelpfulReview) => ({ + ...prevHelpfulReview, + [reviewId]: isHelpful, + })); + }; + + // Handle marking reviews as helpful +// const markReviewHelpful = async (reviewId, isHelpful) => { +// try { +// await fetch(`${apiUrl}/api/reviews/${reviewId}/helpful`, { +// method: 'PATCH', +// headers: { 'Content-Type': 'application/json' }, +// body: JSON.stringify({ helpful: isHelpful }), +// }); +// setHelpfulReview({ ...helpfulReview, [reviewId]: isHelpful }); +// } catch (error) { +// console.error('Error marking review as helpful:', error); +// } +// }; + + + // Apply filter and sort to reviews + const filteredReviews = reviews + .filter((review) => filterRating === 0 || review.rating === filterRating) + .sort((a, b) => { + if (sortOrder === 'newest') return new Date(b.date) - new Date(a.date); + if (sortOrder === 'oldest') return new Date(a.date) - new Date(b.date); + if (sortOrder === 'highest') return b.rating - a.rating; + if (sortOrder === 'lowest') return a.rating - b.rating; + return 0; + }); + + // Rating distribution graph data + const chartData = { + labels: ['1 Star', '2 Stars', '3 Stars', '4 Stars', '5 Stars'], + datasets: [ + { + label: 'Number of Ratings', + data: ratingDistribution, + backgroundColor: ['#f87171', '#fbbf24', '#facc15', '#4ade80', '#22c55e'], + }, + ], + }; + + return ( +
+

Customer Reviews

+ + {/* Average Rating and Rating Distribution */} +
+ {averageRating.toFixed(1)} + +
+ + + {/* Filter and Sort Options */} +
+ {/* Filter by Rating */} +
+ + +
+ + {/* Sort Options */} +
+ + +
+
+ + {/* Review Form */} +
+

Submit Your Review

+
+ + {[1, 2, 3, 4, 5].map((star) => ( + setNewReview({ ...newReview, rating: star })} + /> + ))} +
+ setNewReview({ ...newReview, title: e.target.value })} + className="mb-4 p-2 border rounded w-full" + /> +