diff --git a/app/components/reviews/ReviewsList.tsx b/app/components/reviews/ReviewsList.tsx new file mode 100644 index 0000000..a21772d --- /dev/null +++ b/app/components/reviews/ReviewsList.tsx @@ -0,0 +1,180 @@ +'use client'; + +import { useMemo, useState } from 'react'; +import { ChevronLeft, ChevronRight, Star } from 'lucide-react'; + +export interface Review { + id: string; + authorName: string; + authorInitials: string; + stars: number; + comment: string; + postedAt: string; +} + +interface ReviewsListProps { + reviews: Review[]; + pageSize?: number; +} + +function StarRow({ value }: { value: number }) { + return ( +
+ {[1, 2, 3, 4, 5].map((star) => { + const filled = star <= value; + return ( +
+ ); +} + +export default function ReviewsList({ reviews, pageSize = 5 }: ReviewsListProps) { + const [page, setPage] = useState(1); + + const summary = useMemo(() => { + if (reviews.length === 0) { + return { average: 0, total: 0, distribution: [0, 0, 0, 0, 0] as number[] }; + } + const distribution: number[] = [0, 0, 0, 0, 0]; + let totalStars = 0; + for (const review of reviews) { + const clamped = Math.max(1, Math.min(5, Math.round(review.stars))); + distribution[clamped - 1]! += 1; + totalStars += review.stars; + } + return { + average: totalStars / reviews.length, + total: reviews.length, + distribution, + }; + }, [reviews]); + + const totalPages = Math.max(1, Math.ceil(reviews.length / pageSize)); + const currentPage = Math.min(page, totalPages); + const startIndex = (currentPage - 1) * pageSize; + const visible = reviews.slice(startIndex, startIndex + pageSize); + + const goPrev = () => setPage((p) => Math.max(1, p - 1)); + const goNext = () => setPage((p) => Math.min(totalPages, p + 1)); + + return ( +
+
+
+

Client reviews

+

+ What people said after working with this artist. +

+
+ +
+
+ {summary.average.toFixed(1)} +
+
+ +

+ {summary.total} review{summary.total === 1 ? '' : 's'} +

+
+
+
+ +
+ {[5, 4, 3, 2, 1].map((star) => { + const count = summary.distribution[star - 1] ?? 0; + const percent = summary.total === 0 ? 0 : Math.round((count / summary.total) * 100); + return ( +
+ {star} +
+ + {visible.length === 0 ? ( +

+ No reviews yet. +

+ ) : ( + + )} + + {totalPages > 1 && ( + + )} +
+ ); +}