Code
tsx 78 lines 2.72 KB
Raw
1 import { AvatarImage, AvatarFallback, Avatar } from "@/components/ui/avatar";
2 import { Product, Review as ReviewType } from "@/lib/types";
3 import { FiveStarRating } from "./five-star-rating";
4 import { AIReviewSummary } from "./ai-review-summary";
5
6 export async function Reviews({ product }: { product: Product }) {
7 // Sort reviews by date descending (newest first)
8 const sortedReviews = [...product.reviews].sort(
9 (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
10 );
11 return (
12 <div className="mx-auto px-4 md:px-6 max-w-2xl grid gap-12">
13 <AIReviewSummary product={product} />
14 {sortedReviews.map((review) => (
15 <div key={review.review}>
16 <Review key={review.review} review={review} />
17 </div>
18 ))}
19 </div>
20 );
21 }
22
23 export function Review({ review }: { review: ReviewType }) {
24 const date = new Date(review.date);
25 return (
26 <div className="flex gap-4">
27 <Avatar className="w-10 h-10 border">
28 <AvatarImage alt="@shadcn" src="/placeholder-user.jpg" />
29 <AvatarFallback>CN</AvatarFallback>
30 </Avatar>
31 <div className="grid gap-4">
32 <div className="flex gap-4 items-start">
33 <div className="grid gap-0.5 text-sm">
34 <h3 className="font-semibold">{review.authorName}</h3>
35 <time
36 className="text-sm text-gray-500 dark:text-gray-400"
37 suppressHydrationWarning
38 >
39 {timeAgo(date)}
40 </time>
41 </div>
42 <div className="flex items-center gap-0.5 ml-auto">
43 <FiveStarRating rating={review.stars} />
44 </div>
45 </div>
46 <div className="text-sm leading-loose text-gray-500 dark:text-gray-400">
47 <p>{review.review}</p>
48 </div>
49 </div>
50 </div>
51 );
52 }
53
54 /**
55 * You probably want to wrap the parent element of this component with `suppressHydrationWarning`
56 */
57 const timeAgo = (date: Date, suffix = true) => {
58 const now = new Date();
59 const seconds = Math.floor((now.getTime() - date.getTime()) / 1000);
60
61 if (seconds < 60) return "Just now";
62 const minutes = Math.floor(seconds / 60);
63 if (minutes < 60)
64 return `${minutes} minute${
65 minutes !== 1 ? "s" : ""
66 }${suffix ? " ago" : ""}`;
67 const hours = Math.floor(minutes / 60);
68 if (hours < 24)
69 return `${hours} hour${hours !== 1 ? "s" : ""}${suffix ? " ago" : ""}`;
70 const days = Math.floor(hours / 24);
71 if (days < 30)
72 return `${days} day${days !== 1 ? "s" : ""}${suffix ? " ago" : ""}`;
73 const months = Math.floor(days / 30);
74 if (months < 12)
75 return `${months} month${months !== 1 ? "s" : ""}${suffix ? " ago" : ""}`;
76 const years = Math.floor(months / 12);
77 return `${years} year${years !== 1 ? "s" : ""}${suffix ? " ago" : ""}`;
78 };