Google Review Branding updated
This commit is contained in:
parent
d1d28b9de5
commit
6db2f00900
@ -1,9 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from "react"
|
||||
import Link from "next/link"
|
||||
import { Swiper, SwiperSlide } from "swiper/react"
|
||||
import { Autoplay, Pagination, Navigation } from "swiper/modules"
|
||||
import GallerySlider1 from '@/components/slider/GallerySlider1'
|
||||
import GoogleReviewsBranding from "@/components/GoogleReviewsBranding"
|
||||
|
||||
const swiperOptions = {
|
||||
modules: [Autoplay, Pagination, Navigation],
|
||||
@ -24,7 +26,83 @@ const swiperOptions = {
|
||||
},
|
||||
}
|
||||
|
||||
const testimonialSwiperOptions = {
|
||||
modules: [Autoplay],
|
||||
slidesPerView: 3,
|
||||
spaceBetween: 20,
|
||||
loop: true,
|
||||
autoplay: {
|
||||
delay: 2000,
|
||||
disableOnInteraction: false,
|
||||
pauseOnMouseEnter: false,
|
||||
},
|
||||
breakpoints: {
|
||||
0: { slidesPerView: 1 },
|
||||
768: { slidesPerView: 2 },
|
||||
1024: { slidesPerView: 3 },
|
||||
},
|
||||
};
|
||||
|
||||
export default function AboutContent() {
|
||||
const [reviews, setReviews] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [expandedReview, setExpandedReview] = useState(null);
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
async function loadReviews() {
|
||||
try {
|
||||
const res = await fetch("/api/reviews");
|
||||
const data = await res.json();
|
||||
|
||||
const cleaned = (data.reviews || []).filter(r =>
|
||||
(r.text ||
|
||||
r.description ||
|
||||
r.snippet ||
|
||||
r.review_text ||
|
||||
r.body ||
|
||||
r.content) &&
|
||||
r.rating >= 4
|
||||
);
|
||||
|
||||
setReviews(cleaned);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch reviews", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
loadReviews();
|
||||
}, []);
|
||||
|
||||
const displayedReviews = reviews.length > 0 && reviews.length < 3
|
||||
? [...reviews, ...reviews, ...reviews]
|
||||
: reviews;
|
||||
|
||||
function renderStars(rating) {
|
||||
return [...Array(5)].map((_, i) => (
|
||||
<span key={i} className={`fa fa-star ${i < rating ? "text-warning" : ""}`}></span>
|
||||
));
|
||||
}
|
||||
|
||||
function getReviewText(r) {
|
||||
return r.text || r.description || r.snippet || r.review_text || r.body || r.content || "";
|
||||
}
|
||||
|
||||
function truncateText(text) {
|
||||
return text.length > 150 ? text.substring(0, 150) + "..." : text;
|
||||
}
|
||||
|
||||
function getProfileImage(r) {
|
||||
const url = r.profile_photo_url || r.author_profile_photo_url || r.user?.thumbnail;
|
||||
if (!url) return "/default-user.png";
|
||||
return url.startsWith("http") ? url : `https://lh3.googleusercontent.com/${url}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -130,88 +208,83 @@ export default function AboutContent() {
|
||||
<div className="auto-container">
|
||||
<div className="sec-title centered">
|
||||
<div className="title">Google Reviews</div>
|
||||
<h2>Hear from our happy customers</h2>
|
||||
<h2>What Our Happy Customers Say</h2>
|
||||
<div className="separate"></div>
|
||||
<GoogleReviewsBranding centered={true} />
|
||||
</div>
|
||||
|
||||
<div className="inner-container">
|
||||
<Swiper {...swiperOptions} className="single-item-carousel">
|
||||
<SwiperSlide>
|
||||
<div className="testimonial-block-two">
|
||||
<div className="inner-box">
|
||||
<div className="rating gap-1 mb-3">
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
</div>
|
||||
<div className="text">“Absolutely love this place! Every blend tastes fresh and natural. The flavors pop, and you can really tell they use quality ingredients. Sixty5 Street never disappoints.”</div>
|
||||
<div className="designation">— Emily R.</div>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="testimonial-block-two">
|
||||
<div className="inner-box">
|
||||
<div className="rating gap-1 mb-3">
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
</div>
|
||||
<div className="text">“The perfect spot when you need something refreshing. Their fruit mixes are vibrant, clean, and full of energy. I always leave feeling great.”</div>
|
||||
<div className="designation">— Jason M.</div>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="testimonial-block-two">
|
||||
<div className="inner-box">
|
||||
<div className="rating gap-1 mb-3">
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
</div>
|
||||
<div className="text">“Sixty5 Street has mastered the art of fresh flavor. The bowls are colorful, the drinks are delicious, and everything feels thoughtfully prepared. A must-try!”</div>
|
||||
<div className="designation">— Sofia L.</div>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="testimonial-block-two">
|
||||
<div className="inner-box">
|
||||
<div className="rating gap-1 mb-3">
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
</div>
|
||||
<div className="text">“Consistently amazing! The blends are smooth, balanced, and not overly sweet. You can taste the real fruit in every sip. Highly recommend for healthy cravings.”</div>
|
||||
<div className="designation">— David P.</div>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="testimonial-block-two">
|
||||
<div className="inner-box">
|
||||
<div className="rating gap-1 mb-3">
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
<span className="fa fa-star"></span>
|
||||
</div>
|
||||
<div className="text">“Super fresh, super tasty. The street-style vibe makes the whole experience fun and lively. Sixty5 Street has quickly become one of my favorite places to grab a flavorful drink.”</div>
|
||||
<div className="designation">— Ava T.</div>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
{loading && <p className="text-center">Loading reviews...</p>}
|
||||
|
||||
{!loading && isClient && displayedReviews.length > 0 && (
|
||||
<Swiper {...testimonialSwiperOptions} className="single-item-carousel">
|
||||
{displayedReviews.map((r, index) => {
|
||||
const fullText = getReviewText(r);
|
||||
const isExpanded = expandedReview === index;
|
||||
|
||||
return (
|
||||
<SwiperSlide key={index}>
|
||||
<div className="google-review-card equal-height">
|
||||
<div className="google-review-header">
|
||||
<div className="google-avatar">
|
||||
<img
|
||||
src={getProfileImage(r)}
|
||||
alt={r.author_name || r.user?.name || "User"}
|
||||
onError={(e) => (e.target.src = "/default-user.png")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="google-user-info">
|
||||
<h4 className="google-name">
|
||||
{r.author_name || r.user?.name || "Customer"}
|
||||
</h4>
|
||||
<div className="google-stars">{renderStars(r.rating)}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="google-text">
|
||||
{isExpanded ? fullText : truncateText(fullText)}
|
||||
</p>
|
||||
|
||||
{r.images &&
|
||||
r.images.length > 0 &&
|
||||
r.images.some(img => img && img !== "ky") && (
|
||||
<div className="google-review-images">
|
||||
{r.images.map((img, i) => {
|
||||
if (!img || img === "ky") return null;
|
||||
const fixedImg = img.startsWith("http")
|
||||
? img
|
||||
: `https://lh3.googleusercontent.com/${img}`;
|
||||
return (
|
||||
<img
|
||||
key={i}
|
||||
src={fixedImg}
|
||||
alt="Review image"
|
||||
className="google-review-photo"
|
||||
onError={(e) => (e.target.style.display = "none")}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{fullText.length > 150 && (
|
||||
<button
|
||||
className="read-more-btn mt-3"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setExpandedReview(isExpanded ? null : index);
|
||||
}}
|
||||
>
|
||||
{isExpanded ? "Read Less" : "Read More"}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
);
|
||||
})}
|
||||
</Swiper>
|
||||
)}
|
||||
|
||||
<div className="btns-box text-center mt-4">
|
||||
<Link
|
||||
|
||||
56
components/GoogleReviewsBranding.js
Normal file
56
components/GoogleReviewsBranding.js
Normal file
@ -0,0 +1,56 @@
|
||||
import React from 'react';
|
||||
|
||||
const GoogleReviewsBranding = ({ centered = false }) => {
|
||||
return (
|
||||
<div className="google-reviews-branding" style={{
|
||||
marginTop: '15px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: centered ? 'center' : 'flex-start',
|
||||
gap: '0px'
|
||||
}}>
|
||||
<div style={{
|
||||
fontSize: '33px',
|
||||
fontWeight: '700',
|
||||
lineHeight: '1.2',
|
||||
letterSpacing: '3px',
|
||||
fontFamily: '"Product Sans", "Google Sans", Roboto, Arial, sans-serif',
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
<span style={{ color: '#4285F4' }}>G</span>
|
||||
<span style={{ color: '#EA4335' }}>o</span>
|
||||
<span style={{ color: '#FBBC04' }}>o</span>
|
||||
<span style={{ color: '#4285F4' }}>g</span>
|
||||
<span style={{ color: '#34A853' }}>l</span>
|
||||
<span style={{ color: '#EA4335' }}>e</span>
|
||||
</div>
|
||||
<div style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: '700',
|
||||
lineHeight: '1.2',
|
||||
fontFamily: '"Product Sans", "Google Sans", Roboto, Arial, sans-serif',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '5px',
|
||||
marginTop: '-2px'
|
||||
}}>
|
||||
<span style={{ color: '#5F6368' }}>Reviews</span>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
gap: '1px',
|
||||
fontSize: '14px',
|
||||
lineHeight: '1'
|
||||
}}>
|
||||
<span style={{ color: '#FBBC04' }}>★</span>
|
||||
<span style={{ color: '#FBBC04' }}>★</span>
|
||||
<span style={{ color: '#FBBC04' }}>★</span>
|
||||
<span style={{ color: '#FBBC04' }}>★</span>
|
||||
<span style={{ color: '#FBBC04' }}>★</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GoogleReviewsBranding;
|
||||
@ -6,6 +6,7 @@ import { Swiper, SwiperSlide } from "swiper/react";
|
||||
|
||||
import "swiper/css";
|
||||
import "swiper/css/autoplay";
|
||||
import GoogleReviewsBranding from "@/components/GoogleReviewsBranding";
|
||||
|
||||
const swiperOptions = {
|
||||
modules: [Autoplay],
|
||||
@ -97,6 +98,7 @@ export default function Testimonial() {
|
||||
<div className="title">Google Reviews</div>
|
||||
<h2>Hear from our happy customers</h2>
|
||||
<div className="separate"></div>
|
||||
<GoogleReviewsBranding centered={true}/>
|
||||
</div>
|
||||
|
||||
<div className="inner-container">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user