Skip to content

Commit 8dc590f

Browse files
Merge pull request #384 from adepat06/feat/delete-review
feat: allow users to delete their own reviews
2 parents 95e3599 + e1db258 commit 8dc590f

3 files changed

Lines changed: 128 additions & 42 deletions

File tree

backend/controller/reviewController.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,41 @@ export const getProductReviews = async (req, res) => {
152152
console.log(error);
153153
res.status(500).json({ message: "Server error" });
154154
}
155+
};
156+
157+
export const deleteReview = async (req, res) => {
158+
try {
159+
const { reviewId } = req.params;
160+
161+
if (!mongoose.Types.ObjectId.isValid(reviewId)) {
162+
return res.status(400).json({ message: "Invalid review ID" });
163+
}
164+
165+
const review = await Review.findById(reviewId);
166+
167+
if (!review) {
168+
return res.status(404).json({ message: "Review not found" });
169+
}
170+
171+
if (review.userId.toString() !== req.userId) {
172+
return res.status(403).json({
173+
message: "You are not authorized to delete this review",
174+
});
175+
}
176+
177+
await review.deleteOne();
178+
179+
const { avgRating, reviewCount } = await recalculateProductRating(
180+
review.productId
181+
);
182+
183+
return res.status(200).json({
184+
message: "Review deleted successfully",
185+
avgRating,
186+
reviewCount,
187+
});
188+
} catch (error) {
189+
console.log(error);
190+
res.status(500).json({ message: "Server error" });
191+
}
155192
};

backend/routes/reviewRoute.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,30 @@ import express from "express";
22
import {
33
addReview,
44
getProductReviews,
5+
deleteReview,
56
} from "../controller/reviewController.js";
6-
77
import authUser from "../middleware/isAuth.js";
8-
98
import validateRequest from "../middleware/validateRequest.js";
109
import { addReviewSchema } from "../validators/authSchemas.js";
1110
import { userRateLimiter } from "../middleware/rateLimiters.js";
1211

1312
const reviewRouter = express.Router();
1413

15-
reviewRouter.post("/add", authUser, userRateLimiter, validateRequest(addReviewSchema), addReview);
14+
reviewRouter.post(
15+
"/add",
16+
authUser,
17+
userRateLimiter,
18+
validateRequest(addReviewSchema),
19+
addReview
20+
);
1621

1722
reviewRouter.get("/:productId", getProductReviews);
1823

19-
export default reviewRouter;
24+
reviewRouter.delete(
25+
"/:reviewId",
26+
authUser,
27+
userRateLimiter,
28+
deleteReview
29+
);
30+
31+
export default reviewRouter;

frontend/src/pages/ProductDetail.jsx

Lines changed: 75 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,30 @@ function ProductDetail() {
203203
setIsEditingReview(true);
204204
};
205205

206+
const handleDeleteReview = async () => {
207+
if (!userReview) return;
208+
209+
const confirmed = window.confirm(
210+
'Are you sure you want to delete your review?'
211+
);
212+
213+
if (!confirmed) return;
214+
215+
try {
216+
const response = await apiConfig.delete(`/review/${userReview._id}`);
217+
218+
toast.success(response.data.message);
219+
220+
setReviewComment('');
221+
setReviewRating(5);
222+
setIsEditingReview(false);
223+
224+
fetchReviews();
225+
} catch {
226+
// API errors are shown by the global interceptor.
227+
}
228+
};
229+
206230
const handleCancelEdit = () => {
207231
setReviewComment('');
208232
setReviewRating(5);
@@ -567,45 +591,58 @@ function ProductDetail() {
567591
className="space-y-6"
568592
>
569593
{userReview && !isEditingReview ? (
570-
<div className="bg-slate-100 dark:bg-gray-800/50 p-6 rounded-xl border border-cyan-400/40">
571-
<div className="flex items-center justify-between mb-4">
572-
<h3 className="text-xl font-semibold text-slate-900 dark:text-white">
573-
Your Review
574-
</h3>
575-
<button
576-
type="button"
577-
onClick={handleEditReview}
578-
className="text-cyan-400 hover:text-cyan-300 text-sm font-medium"
579-
>
580-
Edit Review
581-
</button>
582-
</div>
583-
<div className="flex items-center gap-1 mb-2" aria-hidden="true">
584-
{[...Array(userReview.rating)].map((_, i) => (
585-
<FaStar key={i} className="text-yellow-400" />
586-
))}
587-
588-
{/* Integrated Sentiment Badge for Current User's Review */}
589-
{userReview.sentimentLabel === 'Positive' && (
590-
<span className="ml-3 px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400 border border-green-200 dark:border-green-800">
591-
Helpful Positive
592-
</span>
593-
)}
594-
{userReview.sentimentLabel === 'Negative' && (
595-
<span className="ml-3 px-2 py-0.5 rounded text-xs font-medium bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400 border border-red-200 dark:border-red-800">
596-
Critical Review
597-
</span>
598-
)}
594+
<div className="bg-slate-100 dark:bg-gray-800/50 p-6 rounded-xl border border-cyan-400/40">
595+
<div className="flex items-center justify-between mb-4">
596+
<h3 className="text-xl font-semibold text-slate-900 dark:text-white">
597+
Your Review
598+
</h3>
599+
600+
<div className="flex items-center gap-3">
601+
<button
602+
type="button"
603+
onClick={handleEditReview}
604+
className="text-cyan-400 hover:text-cyan-300 text-sm font-medium"
605+
>
606+
Edit Review
607+
</button>
599608

600-
</div>
601-
<p className="text-slate-700 dark:text-gray-300">
602-
{userReview.comment}
603-
</p>
604-
<p className="text-slate-500 dark:text-gray-400 text-sm mt-3">
605-
{new Date(userReview.createdAt).toLocaleDateString()}
606-
</p>
607-
</div>
608-
) : (
609+
<button
610+
type="button"
611+
onClick={handleDeleteReview}
612+
className="text-red-500 hover:text-red-400 text-sm font-medium"
613+
>
614+
Delete Review
615+
</button>
616+
</div>
617+
</div>
618+
619+
<div className="flex items-center gap-1 mb-2" aria-hidden="true">
620+
{[...Array(userReview.rating)].map((_, i) => (
621+
<FaStar key={i} className="text-yellow-400" />
622+
))}
623+
624+
{userReview.sentimentLabel === 'Positive' && (
625+
<span className="ml-3 px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400 border border-green-200 dark:border-green-800">
626+
Helpful Positive
627+
</span>
628+
)}
629+
630+
{userReview.sentimentLabel === 'Negative' && (
631+
<span className="ml-3 px-2 py-0.5 rounded text-xs font-medium bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400 border border-red-200 dark:border-red-800">
632+
Critical Review
633+
</span>
634+
)}
635+
</div>
636+
637+
<p className="text-slate-700 dark:text-gray-300">
638+
{userReview.comment}
639+
</p>
640+
641+
<p className="text-slate-500 dark:text-gray-400 text-sm mt-3">
642+
{new Date(userReview.createdAt).toLocaleDateString()}
643+
</p>
644+
</div>
645+
) : (
609646
<div className="bg-slate-100 dark:bg-gray-800/50 p-6 rounded-xl border border-slate-200 dark:border-gray-700/60">
610647
<h3 className="text-xl font-semibold mb-4 text-slate-900 dark:text-white">
611648
{isEditingReview ? 'Edit Your Review' : 'Write a Review'}

0 commit comments

Comments
 (0)