'use client' import { useState, useEffect, useCallback } from 'react' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Star, Filter, Plus } from 'lucide-react' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import ReviewCard from './ReviewCard' import ReviewForm from './ReviewForm' import { toast } from 'sonner' import { useSession } from 'next-auth/react' interface Review { id: string rating: number title?: string comment?: string images: string[] isVerified: boolean helpfulVotes: number createdAt: string userHasVoted?: boolean // Track if current user has voted this review as helpful user: { id: string name: string image?: string } _count?: { helpfulVotedBy: number } } interface ReviewsListProps { productId: string productName: string averageRating?: number totalReviews?: number ratingBreakdown?: { [key: number]: number } disableImageUpload?: boolean } export default function ReviewsList({ productId, productName, averageRating = 0, totalReviews = 0, ratingBreakdown = {}, disableImageUpload = false }: ReviewsListProps) { const { data: session } = useSession() const [reviews, setReviews] = useState([]) const [loading, setLoading] = useState(true) const [page, setPage] = useState(1) const [hasMore, setHasMore] = useState(true) const [ratingFilter, setRatingFilter] = useState('all') const [showForm, setShowForm] = useState(false) const [editingReview, setEditingReview] = useState(null) const [userReview, setUserReview] = useState(null) const [allReviews, setAllReviews] = useState([]) const [calculatedStats, setCalculatedStats] = useState({ averageRating: 0, totalReviews: 0, ratingBreakdown: {} as { [key: number]: number } }) const scrollToReviews = () => { const reviewsSection = document.getElementById('reviews-section') if (reviewsSection) { reviewsSection.scrollIntoView({ behavior: 'smooth', block: 'start' }) // If there are no reviews, automatically show the form if (calculatedStats.totalReviews === 0) { setTimeout(() => setShowForm(true), 500) } } } const calculateStats = useCallback((allReviewsData: Review[]) => { const total = allReviewsData.length if (total === 0) { setCalculatedStats({ averageRating: 0, totalReviews: 0, ratingBreakdown: {} }) return } const sum = allReviewsData.reduce((acc, review) => acc + review.rating, 0) const avg = sum / total const breakdown: { [key: number]: number } = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 } allReviewsData.forEach(review => { breakdown[review.rating] = (breakdown[review.rating] || 0) + 1 }) setCalculatedStats({ averageRating: avg, totalReviews: total, ratingBreakdown: breakdown }) }, []) const fetchAllReviewsForStats = useCallback(async () => { try { const params = new URLSearchParams({ productId, limit: '1000' // Get all reviews for stats }) const response = await fetch(`/api/reviews?${params}`) const data = await response.json() if (response.ok) { setAllReviews(data.reviews) calculateStats(data.reviews) } } catch (error) { console.error('Failed to fetch all reviews for stats:', error) } }, [productId, calculateStats]) const fetchReviews = useCallback(async (resetList = false) => { try { setLoading(true) const params = new URLSearchParams({ productId, page: resetList ? '1' : page.toString(), limit: '10' }) if (ratingFilter !== 'all') { params.append('rating', ratingFilter) } const response = await fetch(`/api/reviews?${params}`) const data = await response.json() if (response.ok) { if (resetList) { setReviews(data.reviews) setPage(1) } else { setReviews(prev => [...prev, ...data.reviews]) } setHasMore(data.pagination.page < data.pagination.pages) // Check if current user has a review if (session?.user) { const userReviewInList = data.reviews.find( (review: Review) => review.user.id === session.user.id ) setUserReview(userReviewInList || null) } // If this is the first page, also fetch all reviews for stats if (resetList) { fetchAllReviewsForStats() } } else { toast.error('Failed to load reviews') } } catch (error) { toast.error('Failed to load reviews') } finally { setLoading(false) } }, [productId, page, ratingFilter, session?.user, fetchAllReviewsForStats]) useEffect(() => { fetchReviews(true) }, [fetchReviews]) const loadMore = () => { setPage(prev => prev + 1) fetchReviews() } const handleReviewSubmit = (newReview: Review) => { setShowForm(false) setEditingReview(null) setUserReview(newReview) fetchReviews(true) // Refresh the list } const handleEdit = (review: Review) => { setEditingReview(review) setShowForm(true) } const handleDelete = async (reviewId: string) => { if (!confirm('Are you sure you want to delete this review?')) return try { const response = await fetch(`/api/reviews/${reviewId}`, { method: 'DELETE' }) if (response.ok) { toast.success('Review deleted successfully') setReviews(prev => prev.filter(review => review.id !== reviewId)) setUserReview(null) } else { const data = await response.json() toast.error(data.error || 'Failed to delete review') } } catch (error) { toast.error('Failed to delete review') } } const handleHelpfulVote = async (reviewId: string) => { if (!session?.user) { toast.error('Please sign in to vote') return } try { const response = await fetch(`/api/reviews/${reviewId}/helpful`, { method: 'POST' }) if (response.ok) { const data = await response.json() // Update the review in the list with new vote status and count setReviews(prev => prev.map(review => review.id === reviewId ? { ...review, userHasVoted: data.hasVoted, helpfulVotes: data.totalVotes, _count: { ...review._count, helpfulVotedBy: data.totalVotes } } : review )) toast.success(data.hasVoted ? 'Marked as helpful' : 'Removed helpful vote') } else { const data = await response.json() toast.error(data.error || 'Failed to vote') } } catch (error) { toast.error('Failed to vote') } } const renderRatingBreakdown = () => { if (!calculatedStats.totalReviews) return null return (
{[5, 4, 3, 2, 1].map(rating => { const count = calculatedStats.ratingBreakdown[rating] || 0 const percentage = calculatedStats.totalReviews > 0 ? (count / calculatedStats.totalReviews) * 100 : 0 return (
{rating}
{count}
) })}
) } return (
{/* Reviews Summary - Only show if there are reviews */} {calculatedStats.totalReviews > 0 && ( Customer Reviews {session && !userReview && !showForm && ( )}
{/* Overall Rating */}
{calculatedStats.averageRating.toFixed(1)}
{Array.from({ length: 5 }, (_, index) => ( ))}

{calculatedStats.totalReviews} reviews

{/* Rating Breakdown */}

Rating Breakdown

{renderRatingBreakdown()}
)} {/* No Reviews State - Show when there are no reviews */} {calculatedStats.totalReviews === 0 && !loading && (
{Array.from({ length: 5 }, (_, index) => ( ))}

No reviews yet

Be the first to review this product

{session ? ( ) : (

Sign in to write a review

)}
)} {/* Review Form */} {showForm && ( { setShowForm(false) setEditingReview(null) }} /> )} {/* Filters and Reviews List */}

All Reviews ({calculatedStats.totalReviews})

{/* Reviews */} {loading && reviews.length === 0 ? (

Loading reviews...

) : reviews.length === 0 ? (

No reviews yet

{session && !userReview && ( )}
) : (
{reviews.map((review) => ( ))} {hasMore && (
)}
)}
) }