2026-03-06 23:38:27 +05:30

227 lines
13 KiB
TypeScript

"use client";
import React, { useState, useRef, useEffect } from 'react';
import Slider from 'react-slick';
import Link from 'next/link';
import GsapReveal from '@/components/common/GsapReveal';
import GoogleReviewsBranding from '@/components/common/GoogleReviewsBranding';
const Testimonial = () => {
const [reviews, setReviews] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const sliderRef = useRef<Slider>(null);
useEffect(() => {
async function loadReviews() {
try {
const res = await fetch("/api/reviews");
const data = await res.json();
// Filter for reviews with text (showing ALL reviews as requested)
const cleaned = (data.reviews || []).filter((r: any) =>
(r.snippet || r.text || r.review_text || r.description || r.body || r.content)
);
setReviews(cleaned);
} catch (error) {
console.error("Failed to fetch reviews", error);
} finally {
setLoading(false);
}
}
loadReviews();
}, []);
// If reviews are sparse, duplicate them to ensure infinite slider works perfectly
const displayedReviews = reviews.length > 0 && reviews.length < 3
? [...reviews, ...reviews, ...reviews]
: reviews;
const settings = {
dots: false,
arrows: false,
infinite: displayedReviews.length > 1,
speed: 600,
slidesToShow: 1,
slidesToScroll: 1,
autoplay: true,
autoplaySpeed: 5000,
responsive: [
{
breakpoint: 1024,
settings: {
slidesToShow: 1,
}
}
]
};
function getReviewText(r: any) {
return r.text || r.description || r.snippet || r.review_text || r.body || r.content || "";
}
function getProfileImage(r: any) {
const url = r.profile_photo_url || r.author_profile_photo_url || r.user?.thumbnail || r.user?.profile_photo_url || r.thumbnail || r.profile_picture || r.avatar || r.author_image;
if (!url) return null;
if (url.startsWith("http")) return url;
return `https://lh3.googleusercontent.com/${url}`;
}
function getInitials(name: string) {
if (!name) return "U";
return name.split(' ').map(n => n.charAt(0)).join('').substring(0, 2).toUpperCase();
}
return (
<div className="testimonial1-section-area sp1" style={{
backgroundImage: 'url(/assets/img/home/bg/bg-4.webp)',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
backgroundAttachment: 'fixed',
position: 'relative'
}}>
<div className="section-overlay"></div>
<div className="container" style={{ position: 'relative', zIndex: 1 }}>
{/* Section Header */}
<div className="row">
<div className="col-lg-7 m-auto">
<div className="testimonial-header text-center heading2 space-margin60">
<GsapReveal y={20}>
<h5><span><img src="/favicon.ico" alt="" /></span>Testimonials</h5>
</GsapReveal>
<div className="space20"></div>
<GsapReveal y={20} delay={0.2}>
<h2 className="text-anime-style-3 text-white">What Our Clients Say</h2>
</GsapReveal>
<div className="space20"></div>
<GsapReveal y={10} delay={0.3}>
<div className="d-flex justify-content-center">
<GoogleReviewsBranding />
</div>
</GsapReveal>
</div>
</div>
</div>
{/* Content Row */}
<div className="row align-items-center g-5">
{/* Left: Single Primary Image */}
<div className="col-lg-5">
<GsapReveal y={30} duration={1}>
<div className="testimonial-img-single" style={{ borderRadius: '24px', overflow: 'hidden' }}>
<img src="/assets/img/home/section8/left-img.webp" alt="Testimonials" />
</div>
</GsapReveal>
</div>
{/* Right: Slider */}
<div className="col-lg-7">
<div className="testi-slider-container" style={{ paddingLeft: '20px' }}>
{loading ? (
<div className="text-center py-5">
<div className="spinner-border text-primary" role="status">
<span className="visually-hidden">Loading reviews...</span>
</div>
<p className="mt-3 text-white">Fetching live Google reviews...</p>
</div>
) : displayedReviews.length > 0 ? (
<div className="testi-slider-wrapper" style={{ position: 'relative' }}>
<Slider ref={sliderRef} {...settings}>
{displayedReviews.map((slide, index) => {
const profileImg = getProfileImage(slide);
const name = slide.user?.name || slide.author_name || "Valued Client";
return (
<div key={index} className="testi-card-wrapper" style={{ outline: 'none', padding: '10px' }}>
<div className="testimonial-bubble" style={{
background: '#1a1f2b',
borderRadius: '24px',
padding: '40px',
position: 'relative',
transition: 'transform 0.3s ease'
}}>
<img src="/assets/img/icons/quito1.svg" alt="" style={{ position: 'absolute', right: 30, top: 30, opacity: 0.15, width: 50 }} />
{/* Stars */}
<div className="stars" style={{ display: 'flex', gap: '6px', marginBottom: '25px' }}>
{[...Array(slide.rating || 5)].map((_, i) => (
<i key={i} className="fa-solid fa-star" style={{ color: '#FBBC04' }}></i>
))}
</div>
<p style={{
color: 'rgba(255,255,255,0.95)',
lineHeight: '1.8',
fontWeight: 400,
margin: 0,
fontStyle: 'italic',
overflow: 'hidden',
display: '-webkit-box',
WebkitLineClamp: 4,
WebkitBoxOrient: 'vertical',
textShadow: '0 2px 4px rgba(0,0,0,0.2)'
}}>
&quot;{getReviewText(slide)}&quot;
</p>
</div>
<div className="testi-author-info" style={{ display: 'flex', alignItems: 'center', marginTop: '35px', paddingLeft: '30px' }}>
<div style={{
width: 75,
height: 75,
borderRadius: '50%',
overflow: 'hidden',
border: '4px solid #3779b9',
boxShadow: '0 15px 30px rgba(0,0,0,0.2)',
background: '#252b3a',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0
}}>
{profileImg ? (
<img src={profileImg} alt={name} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
) : (
<span style={{ fontWeight: 'bold', color: '#fff' }}>{getInitials(name)}</span>
)}
</div>
<div style={{ marginLeft: '25px' }}>
<h4 style={{ margin: 0, fontWeight: 700, color: '#fff', letterSpacing: '0.5px' }}>{name}</h4>
{/* <div style={{ display: 'flex', alignItems: 'center', marginTop: '5px' }}>
<i className="fa-brands fa-google" style={{ color: '#3779b9', marginRight: '8px' }}></i>
<p style={{ margin: 0, color: 'rgba(255,255,255,0.5)', fontWeight: 500 }}>Verified Google Reviewer</p>
</div> */}
</div>
</div>
</div>
);
})}
</Slider>
</div>
) : (
<div className="text-center py-5">
<p className="text-white">Professional services you can trust.</p>
</div>
)}
{/* Google Action Button */}
<div className="mt-50 text-center text-lg-start" style={{ paddingLeft: '20px' }}>
<GsapReveal y={20} delay={0.5}>
<Link
href="https://www.google.com/maps/place/Metatroncube+Software+Solutions/@34.0518468,-56.3267266,3z/data=!4m8!3m7!1s0x89d4d7ff0d82df5b:0xf0ca6a97298a109c!8m2!3d43.4510036!4d-80.5909483!9m1!1b1!16s%2Fg%2F11k197xnvf?entry=ttu&g_ep=EgoyMDI2MDMwMi4wIKXMDSoASAFQAw%3D%3D"
target="_blank"
className="vl-btn2"
style={{ display: 'inline-flex', alignItems: 'center', gap: '10px' }}
>
Review us on Google <i className="fa-brands fa-google"></i>
</Link>
</GsapReveal>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default Testimonial;