77 lines
2.0 KiB
TypeScript
77 lines
2.0 KiB
TypeScript
"use client";
|
|
|
|
import { motion } from "framer-motion";
|
|
|
|
type Review = {
|
|
name: string;
|
|
rating: string;
|
|
dateLabel: string;
|
|
quote: string;
|
|
};
|
|
|
|
type Props = {
|
|
reviews: Review[];
|
|
};
|
|
|
|
function StarRating({ rating }: { rating: string }) {
|
|
const score = parseFloat(rating);
|
|
const full = Math.floor(score);
|
|
return (
|
|
<div className="tc-stars" aria-label={`${rating} stars`}>
|
|
{Array.from({ length: 5 }).map((_, i) => (
|
|
<span key={i} className={i < full ? "tc-star filled" : "tc-star"}>
|
|
★
|
|
</span>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ReviewCard({ review }: { review: Review }) {
|
|
return (
|
|
<motion.article
|
|
className="tc-card"
|
|
whileHover={{ y: -6, boxShadow: "0 20px 40px rgba(0,0,0,0.12)" }}
|
|
transition={{ duration: 0.25, ease: "easeOut" }}
|
|
>
|
|
<div className="tc-card-top">
|
|
<StarRating rating={review.rating} />
|
|
<span className="tc-date">{review.dateLabel}</span>
|
|
</div>
|
|
<blockquote className="tc-quote">"{review.quote}"</blockquote>
|
|
<footer className="tc-author">
|
|
<div className="tc-avatar" aria-hidden="true">
|
|
{review.name[0]}
|
|
</div>
|
|
<div>
|
|
<strong className="tc-name">{review.name}</strong>
|
|
<span className="tc-source">Google Review</span>
|
|
</div>
|
|
</footer>
|
|
</motion.article>
|
|
);
|
|
}
|
|
|
|
export function TestimonialsCarousel({ reviews }: Props) {
|
|
// Triple the items for a seamless infinite loop at any scroll speed
|
|
const tripled = [...reviews, ...reviews, ...reviews];
|
|
|
|
return (
|
|
<div className="tc-wrapper">
|
|
<div className="tc-track-container">
|
|
<div className="tc-track">
|
|
{tripled.map((review, i) => (
|
|
<div className="tc-slide" key={i}>
|
|
<ReviewCard review={review} />
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Edge fade overlays */}
|
|
<div className="tc-fade-left" aria-hidden="true" />
|
|
<div className="tc-fade-right" aria-hidden="true" />
|
|
</div>
|
|
);
|
|
}
|