x
This commit is contained in:
1 parent
d325cd8763
commit
c6bf2f70ac
45 files changed
+5195
-338
No files matched your search
@@ -21,6 +21,8 @@ export default function ProductReviews({ productId }: { productId: number }) {
|
||||
const [rating, setRating] = useState(5)
|
||||
const [comment, setComment] = useState('')
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const [filterRating, setFilterRating] = useState<number | null>(null)
|
||||
const [visibleCount, setVisibleCount] = useState(3)
|
||||
|
||||
const fetchReviews = () => {
|
||||
fetch(`${API_BASE}/reviews/product/${productId}`)
|
||||
@@ -68,6 +70,12 @@ export default function ProductReviews({ productId }: { productId: number }) {
|
||||
? (reviews.reduce((acc, r) => acc + r.rating, 0) / reviews.length).toFixed(1)
|
||||
: '0.0'
|
||||
|
||||
const filteredReviews = filterRating
|
||||
? reviews.filter(r => r.rating === filterRating)
|
||||
: reviews
|
||||
|
||||
const visibleReviews = filteredReviews.slice(0, visibleCount)
|
||||
|
||||
return (
|
||||
<div className="pt-16 border-t border-spa-stone/10 space-y-12">
|
||||
<div className="flex flex-col md:flex-row gap-12 items-start">
|
||||
@@ -118,12 +126,47 @@ export default function ProductReviews({ productId }: { productId: number }) {
|
||||
)}
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* Filter Bar */}
|
||||
{reviews.length > 0 && !loading && (
|
||||
<div className="flex gap-2 overflow-x-auto pb-2" style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}>
|
||||
<button
|
||||
onClick={() => { setFilterRating(null); setVisibleCount(3); }}
|
||||
className={`flex-shrink-0 px-4 py-1.5 rounded-full text-xs font-bold transition-all border ${
|
||||
filterRating === null
|
||||
? 'bg-spa-stone text-white border-spa-stone'
|
||||
: 'bg-white text-spa-stone border-spa-stone/20 hover:border-spa-stone/50'
|
||||
}`}
|
||||
>
|
||||
All ({reviews.length})
|
||||
</button>
|
||||
{[5, 4, 3, 2, 1].map(star => {
|
||||
const count = reviews.filter(r => r.rating === star).length;
|
||||
if (count === 0) return null;
|
||||
return (
|
||||
<button
|
||||
key={`filter-${star}`}
|
||||
onClick={() => { setFilterRating(star); setVisibleCount(3); }}
|
||||
className={`flex-shrink-0 flex items-center gap-1 px-4 py-1.5 rounded-full text-xs font-bold transition-all border ${
|
||||
filterRating === star
|
||||
? 'bg-spa-stone text-white border-spa-stone'
|
||||
: 'bg-white text-spa-stone border-spa-stone/20 hover:border-spa-stone/50'
|
||||
}`}
|
||||
>
|
||||
{star} <Star className={`w-3 h-3 ${filterRating === star ? 'fill-white text-white' : 'fill-orange-400 text-orange-400'}`} /> ({count})
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{loading ? (
|
||||
<p className="text-spa-stone animate-pulse text-sm">Loading reviews...</p>
|
||||
) : reviews.length === 0 ? (
|
||||
<p className="text-spa-stone/60 text-sm italic">No reviews yet. Be the first to share your thoughts!</p>
|
||||
) : visibleReviews.length === 0 ? (
|
||||
<p className="text-spa-stone/60 text-sm italic">No reviews found for this rating.</p>
|
||||
) : (
|
||||
reviews.map(review => (
|
||||
visibleReviews.map(review => (
|
||||
<div key={review.id} className="border-b border-spa-stone/10 pb-6 last:border-0">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
{review.customer?.pictureUrl ? (
|
||||
@@ -147,6 +190,17 @@ export default function ProductReviews({ productId }: { productId: number }) {
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
|
||||
{visibleCount < filteredReviews.length && (
|
||||
<div className="text-center pt-4">
|
||||
<button
|
||||
onClick={() => setVisibleCount(prev => prev + 5)}
|
||||
className="px-6 py-2.5 border border-spa-stone/20 text-spa-stone text-xs font-bold tracking-widest uppercase rounded-full hover:border-spa-stone hover:text-pop-black transition-all bg-white"
|
||||
>
|
||||
Show More Reviews
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user