- Create TestimonialsSection with 5 placeholder reviews - Auto-rotates every 5s, pauses on hover, clearInterval cleanup - ChevronLeft/ChevronRight arrow controls and dot indicators - Wire TestimonialsSection into page.tsx between hero and listings
191 lines
5.2 KiB
TypeScript
191 lines
5.2 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect, useRef } from 'react';
|
|
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
|
|
|
// PLACEHOLDER TESTIMONIALS — replace with real client reviews before launch
|
|
const TESTIMONIALS = [
|
|
{
|
|
quote:
|
|
'Working with Teressa made buying our first home in Salt Lake County feel effortless. She guided us through every step with patience and expertise.',
|
|
name: 'Sarah Mitchell',
|
|
},
|
|
{
|
|
quote:
|
|
'Teressa sold our Provo home in under a week — above asking price. Her knowledge of the Utah market is exceptional.',
|
|
name: 'James & Karen Olsen',
|
|
},
|
|
{
|
|
quote:
|
|
'As first-time buyers, we had so many questions. Teressa answered every one and found us the perfect home in our budget.',
|
|
name: 'Tyler Reeves',
|
|
},
|
|
{
|
|
quote:
|
|
'Relocating from out of state is stressful, but Teressa made our transition to Utah smooth and seamless.',
|
|
name: 'Michelle Torres',
|
|
},
|
|
{
|
|
quote:
|
|
"Teressa's negotiating skills saved us thousands. We couldn't be happier with our new home in Herriman.",
|
|
name: 'David & Pam Christensen',
|
|
},
|
|
];
|
|
|
|
export default function TestimonialsSection() {
|
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
const [paused, setPaused] = useState(false);
|
|
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!paused) {
|
|
intervalRef.current = setInterval(() => {
|
|
setActiveIndex((i) => (i + 1) % TESTIMONIALS.length);
|
|
}, 5000);
|
|
}
|
|
|
|
// Cleanup — prevents memory leak on re-render
|
|
return () => {
|
|
if (intervalRef.current) clearInterval(intervalRef.current);
|
|
};
|
|
}, [paused]);
|
|
|
|
function prev() {
|
|
setActiveIndex(
|
|
(i) => (i - 1 + TESTIMONIALS.length) % TESTIMONIALS.length
|
|
);
|
|
}
|
|
|
|
function next() {
|
|
setActiveIndex((i) => (i + 1) % TESTIMONIALS.length);
|
|
}
|
|
|
|
const current = TESTIMONIALS[activeIndex];
|
|
|
|
return (
|
|
<section
|
|
id="about"
|
|
className="testimonials-section"
|
|
onMouseEnter={() => setPaused(true)}
|
|
onMouseLeave={() => setPaused(false)}
|
|
>
|
|
{/* Decorative gold quote mark */}
|
|
<div className="testimonials-quote-mark" aria-hidden="true">
|
|
“
|
|
</div>
|
|
|
|
<blockquote className="testimonials-blockquote">
|
|
<p className="testimonials-quote">{current.quote}</p>
|
|
<footer className="testimonials-attribution">
|
|
— {current.name}
|
|
</footer>
|
|
</blockquote>
|
|
|
|
{/* Arrow controls */}
|
|
<div className="testimonials-arrows">
|
|
<button
|
|
onClick={prev}
|
|
aria-label="Previous testimonial"
|
|
className="testimonials-arrow-btn"
|
|
>
|
|
<ChevronLeft size={24} />
|
|
</button>
|
|
<button
|
|
onClick={next}
|
|
aria-label="Next testimonial"
|
|
className="testimonials-arrow-btn"
|
|
>
|
|
<ChevronRight size={24} />
|
|
</button>
|
|
</div>
|
|
|
|
{/* Dot indicators */}
|
|
<div className="testimonials-dots" role="tablist" aria-label="Testimonials">
|
|
{TESTIMONIALS.map((_, i) => (
|
|
<button
|
|
key={i}
|
|
onClick={() => setActiveIndex(i)}
|
|
role="tab"
|
|
aria-selected={i === activeIndex}
|
|
aria-label={`Go to testimonial ${i + 1}`}
|
|
className="testimonials-dot"
|
|
style={{ opacity: i === activeIndex ? 1 : 0.3 }}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
<style>{`
|
|
.testimonials-section {
|
|
background-color: #FAF9F7;
|
|
padding: 4rem 2rem;
|
|
text-align: center;
|
|
}
|
|
.testimonials-quote-mark {
|
|
font-size: 6rem;
|
|
line-height: 1;
|
|
color: #C9A84C;
|
|
font-family: Georgia, serif;
|
|
margin-bottom: -1rem;
|
|
user-select: none;
|
|
}
|
|
.testimonials-blockquote {
|
|
max-width: 700px;
|
|
margin: 0 auto 2rem;
|
|
padding: 0;
|
|
}
|
|
.testimonials-quote {
|
|
font-size: 1.125rem;
|
|
color: #1B2B4B;
|
|
line-height: 1.8;
|
|
font-style: italic;
|
|
margin-bottom: 1rem;
|
|
}
|
|
.testimonials-attribution {
|
|
font-size: 0.95rem;
|
|
color: #1B2B4B;
|
|
font-weight: 600;
|
|
opacity: 0.8;
|
|
}
|
|
.testimonials-arrows {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 1rem;
|
|
margin-bottom: 1.25rem;
|
|
}
|
|
.testimonials-arrow-btn {
|
|
background: none;
|
|
border: 2px solid #1B2B4B;
|
|
border-radius: 50%;
|
|
width: 40px;
|
|
height: 40px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
color: #1B2B4B;
|
|
transition: background-color 0.2s, color 0.2s;
|
|
}
|
|
.testimonials-arrow-btn:hover {
|
|
background-color: #1B2B4B;
|
|
color: #FAF9F7;
|
|
}
|
|
.testimonials-dots {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 0.5rem;
|
|
}
|
|
.testimonials-dot {
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
background-color: #1B2B4B;
|
|
border: none;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
transition: opacity 0.2s;
|
|
}
|
|
`}</style>
|
|
</section>
|
|
);
|
|
}
|