2026-01-22 00:46:43 +05:30

252 lines
10 KiB
TypeScript

'use client'
import { useState, useEffect } from 'react'
import Image from 'next/image'
import styles from './Testimonials.module.css'
import { Swiper, SwiperSlide } from 'swiper/react'
import { Autoplay, Navigation } from 'swiper/modules'
import 'swiper/css'
import 'swiper/css/navigation'
import { FaStar, FaChevronLeft, FaChevronRight } from 'react-icons/fa'
interface Review {
text?: string;
description?: string;
snippet?: string;
review_text?: string;
body?: string;
content?: string;
rating: number;
profile_photo_url?: string;
author_profile_photo_url?: string;
user?: {
thumbnail?: string;
name?: string;
};
author_name?: string;
}
export default function Testimonials() {
const [reviews, setReviews] = useState<Review[]>([]);
const [loading, setLoading] = useState(true);
const [expandedReview, setExpandedReview] = useState<number | null>(null);
const [swiperInstance, setSwiperInstance] = useState<any>(null);
const testimonial_list_slider: any = {
spaceBetween: 30,
slidesPerView: 3,
navigation: {
prevEl: `.${styles.prevArrow}`,
nextEl: `.${styles.nextArrow}`,
},
pagination: false,
loop: true,
autoplay: {
delay: 3000,
disableOnInteraction: false,
},
modules: [Autoplay, Navigation],
breakpoints: {
0: {
slidesPerView: 1,
},
768: {
slidesPerView: 2,
},
1024: {
slidesPerView: 3,
},
}
};
// Auto-collapse expanded review after 10 seconds and handle autoplay
useEffect(() => {
if (expandedReview !== null) {
// Stop autoplay when a review is expanded
if (swiperInstance && swiperInstance.autoplay) {
swiperInstance.autoplay.stop();
}
const timer = setTimeout(() => {
setExpandedReview(null);
}, 10000); // 10 seconds
return () => {
clearTimeout(timer);
};
} else {
// Resume autoplay when reviews are collapsed
if (swiperInstance && swiperInstance.autoplay) {
swiperInstance.autoplay.start();
}
}
}, [expandedReview, swiperInstance]);
useEffect(() => {
async function loadReviews() {
try {
const res = await fetch("/api/reviews");
if (!res.ok) {
let details = "Unknown error";
try {
const errorData = await res.json();
details = errorData.details || errorData.error || "No details";
} catch (e) { }
console.error(`HTTP error! status: ${res.status} - ${details}`);
setLoading(false);
return;
}
const text = await res.text();
let dataAt;
try {
dataAt = JSON.parse(text);
} catch (e) {
console.error("Home: Invalid JSON response", text.slice(0, 100));
return;
}
const cleaned = (dataAt.reviews || []).filter((r: Review) =>
(r.text || r.description || r.snippet || r.review_text || r.body || r.content) &&
r.rating >= 4
);
setReviews(cleaned);
} catch (error) {
console.error("Home: Failed to fetch reviews", error);
} finally {
setLoading(false);
}
}
loadReviews();
}, []);
const displayedReviews = reviews.length > 0 && reviews.length < 3
? [...reviews, ...reviews, ...reviews]
: reviews;
function renderStars(rating: number) {
return [...Array(5)].map((_, i) => (
<FaStar
key={i}
style={{ color: i < rating ? '#ffc107' : '#e4e5e9', fontSize: '16px', marginRight: '5px' }}
/>
));
}
function getReviewText(r: Review) {
return r.text || r.description || r.snippet || r.review_text || r.body || r.content || "";
}
function truncateText(text: string) {
return text.length > 150 ? text.substring(0, 150) + "..." : text;
}
function getProfileImage(r: Review) {
const url = r.profile_photo_url || r.author_profile_photo_url || r.user?.thumbnail;
if (!url) return null;
return url.startsWith("http") ? url : `https://lh3.googleusercontent.com/${url}`;
}
function getInitials(name: string) {
if (!name) return "U";
return name.split(' ').map(n => n[0]).join('').substring(0, 2).toUpperCase();
}
return (
<section className={styles.section}>
<div className={styles.smallHeading} style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '10px' }}>
<Image src="/images/dinner.png" alt="Antalya Dinner Icon" width={24} height={24} />
<span>ANTALYA</span>
<Image src="/images/eat.png" alt="Antalya Cutlery Icon" width={24} height={24} />
</div>
<h2 className={styles.title}>Testimonials</h2>
<div className={styles.sliderContainer}>
{loading ? (
<div className="text-center" style={{ color: 'var(--color-paragraph)', padding: '40px' , textAlign: 'center'}}>
<p>Loading reviews...</p>
</div>
) : (
<>
<button className={`${styles.navBtn} ${styles.prevArrow}`}>
<FaChevronLeft />
</button>
<Swiper
{...testimonial_list_slider}
onSwiper={setSwiperInstance}
className="testimonial_list"
style={{ paddingBottom: '30px', flex: 1 }}
>
{displayedReviews.map((r, index) => {
const fullText = getReviewText(r);
const isExpanded = expandedReview === index;
const profileImg = getProfileImage(r);
const name = r.user?.name || r.author_name || "Customer";
return (
<SwiperSlide key={index} style={{ height: 'auto' }}>
<div className={styles.card}>
<div className={styles.avatarContainer}>
<div className={styles.authorImageWrapper}>
{profileImg ? (
<img
src={profileImg}
alt={name}
className={styles.authorImage}
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
onError={(e) => ((e.target as HTMLImageElement).src = '/images/placeholder.png')}
/>
) : (
<div className={styles.initials}>
{getInitials(name)}
</div>
)}
</div>
</div>
<p className={styles.name}>{name}</p>
<div className={styles.stars}>
{renderStars(r.rating)}
</div>
<p className={styles.text}>
&quot;{isExpanded ? fullText : truncateText(fullText)}&quot;
</p>
<button
className={styles.readMoreBtn}
onClick={() => setExpandedReview(isExpanded ? null : index)}
>
{isExpanded ? "Read Less" : "Read More"}
</button>
</div>
</SwiperSlide>
);
})}
</Swiper>
<button className={`${styles.navBtn} ${styles.nextArrow}`}>
<FaChevronRight />
</button>
</>
)}
</div>
<div className={styles.buttonContainer}>
<a
href="https://www.google.com/search?sca_esv=fb3147f266a54277&sxsrf=AE3TifPK9lVB_UK5Mt9ko4Ht65w63RgqUQ:1764431326889&si=AMgyJEtREmoPL4P1I5IDCfuA8gybfVI2d5Uj7QMwYCZHKDZ-E3A-qenJpQm2J1V3Wa6_UKYzIQhT0idJrAopWIgZ0RiDDHrNP0RtPpfTzWgU2637exPDNkEZu0WuVN4TEdgoYqqtQwcWsOUIFi4JvvmrZPyybRzrcg%3D%3D&q=Antalya+Turkish+Restaurant+Reviews&sa=X&ved=2ahUKEwic4uDz2peRAxUbTmwGHWwWBRoQ0bkNegQIJRAE&biw=1528&bih=786&dpr=1.25&zx=1764431335803&no_sw_cr=1#lrd=0x882bf532910f4e25:0x4aee10507689253d,3,,,,"
target="_blank"
rel="noopener noreferrer"
className={styles.button}
>
Review Us on Google
</a>
</div>
</section>
)
}