From 1cf4f668334f515942f19b5a1ad05f85805991a8 Mon Sep 17 00:00:00 2001 From: akash Date: Fri, 26 Dec 2025 14:05:50 +0530 Subject: [PATCH] review integration updated --- app/api/reviews/route.js | 43 +++++ app/globals.css | 119 +++++++++++++ app/page.js | 293 +++++++++++++++------------------ components/TestimonialAbout.js | 162 ++++++++++++++---- utility/sliderProps.js | 8 +- 5 files changed, 430 insertions(+), 195 deletions(-) create mode 100644 app/api/reviews/route.js diff --git a/app/api/reviews/route.js b/app/api/reviews/route.js new file mode 100644 index 0000000..70d8fd5 --- /dev/null +++ b/app/api/reviews/route.js @@ -0,0 +1,43 @@ +import { NextResponse } from 'next/server'; + +export async function GET() { + const apiKey = "37eb7f83988cfd76ffb5c5af9adc25652efe5607e39997fc7d0e054d690ef25e"; + const placeId = "ChIJO7FdBHn1K4gR1EklzzybjiU"; + + let allReviews = []; + let nextPageToken = null; + + try { + let pageCount = 0; + while (pageCount < 3) { + pageCount++; + const url = `https://serpapi.com/search.json?engine=google_maps_reviews&hl=en&api_key=${apiKey}&place_id=${placeId}${nextPageToken ? `&next_page_token=${nextPageToken}` : ""}`; + + const response = await fetch(url); + const data = await response.json(); + + if (data.error) { + console.error("Reviews API Error:", data.error); + break; + } + + if (data.reviews && data.reviews.length > 0) { + allReviews = [...allReviews, ...data.reviews]; + } else { + break; + } + + if (!data.serpapi_pagination || !data.serpapi_pagination.next_page_token) { + break; + } + + nextPageToken = data.serpapi_pagination.next_page_token; + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + + return NextResponse.json({ reviews: allReviews, total: allReviews.length }); + } catch (error) { + console.error("Reviews API Unexpected Error:", error); + return NextResponse.json({ error: "Failed to fetch reviews", details: error.message }, { status: 500 }); + } +} diff --git a/app/globals.css b/app/globals.css index f19854b..d091d81 100644 --- a/app/globals.css +++ b/app/globals.css @@ -131,3 +131,122 @@ + +.google-review-card { + background: #fff; + border-radius: 12px; + padding: 25px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05); + border: 1px solid #f0f0f0; + height: 100%; + transition: all 0.3s ease; + display: flex; + flex-direction: column; +} + +.google-review-header { + display: flex; + align-items: center; + gap: 15px; + margin-bottom: 20px; +} + +.google-avatar { + width: 60px; + height: 60px; + border-radius: 50%; + background: #546e7a; + color: #fff; + font-weight: 600; + font-size: 24px; + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + flex-shrink: 0; +} + +.google-avatar img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.google-user-info { + display: flex; + flex-direction: column; +} + +.google-name { + font-size: 18px; + font-weight: 700; + color: #333; + margin: 0 !important; + line-height: 1.2; +} + +.google-stars { + margin-top: 4px; +} + +.google-stars span, +.google-stars i { + font-size: 16px; + color: #ffc107; + margin-right: 2px; +} + +.google-text { + font-size: 15px; + color: #eee; + line-height: 1.6; + margin: 0; + flex-grow: 1; +} + +.google-review-card-home { + padding: 0; +} + +.google-review-card-home .google-name { + color: #fff !important; +} + +.google-review-card-home .google-user-info h4 { + color: #fff !important; +} + +.google-review-card-home .testimonial-text { + color: #fff !important; +} + +/* Ensure stars are visible on white background for About page */ +.testimonial-five-item .google-stars i { + color: #ffc107 !important; +} + +.google-review-header .google-name { + color: inherit; +} + +.read-more-btn { + background: transparent; + border: none; + color: #ffb936; + padding: 15px 0 0 0; + font-size: 16px; + cursor: pointer; + font-weight: 700; + text-align: center; + width: 100%; + display: block; + margin-top: auto; +} + +.read-more-btn:hover { + text-decoration: underline; +} + +.equal-height { + min-height: 380px; +} \ No newline at end of file diff --git a/app/page.js b/app/page.js index b700f8e..1404db2 100644 --- a/app/page.js +++ b/app/page.js @@ -10,7 +10,59 @@ import HeroBanner from "@/components/home/HeroBanner"; import PopularMenu from "@/components/home/PopularMenu"; import MenuCategory from "@/components/home/MenuCategory"; import BlogSection from "@/components/home/BlogSection"; +import { useEffect, useState } from "react"; const page = () => { + const [reviews, setReviews] = useState([]); + const [loading, setLoading] = useState(true); + const [expandedReview, setExpandedReview] = useState(null); + + 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("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) { + return [...Array(5)].map((_, i) => ( + + )); + } + + 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 null; + return url.startsWith("http") ? url : `https://lh3.googleusercontent.com/${url}`; + } + + function getInitials(name) { + if (!name) return "U"; + return name.split(' ').map(n => n[0]).join('').substring(0, 2).toUpperCase(); + } return ( {/* Hero Area Start */} @@ -346,168 +398,97 @@ const page = () => { data-aos-duration={1500} data-aos-offset={50} > -
-
-
- {/* */} -
-
- -
-
- -
-
- -
-
- -
-
- -
-
+ {loading ? ( +
+
+
Loading reviews...
+
+
+ ) : displayedReviews.length > 0 ? ( + 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"; -
-
- The food was exceptional, with the dosas being a particular highlight and deserving of strong recommendation. Their taste was remarkable, representing the best dosas in the entire KWC region. The staff provided polite and attentive service. While the quantity of chutneys and sauces could be improved, the overall dining experience was highly satisfactory. -
- Mohit - {/* web designer */} -
-
-
-
-
- {/* */} -
-
- -
-
- -
-
- -
-
- -
-
- + return ( +
+
+
+
+
+ {profileImg ? ( + {name} (e.target.style.display = 'none')} + style={{ width: '100%', height: '100%', objectFit: 'cover' }} + /> + ) : ( + {getInitials(name)} + )} +
+
+

+ {name} +

+
+ {renderStars(r.rating)} +
+
+
+
+
+

+ {isExpanded ? fullText : truncateText(fullText)} +

+
+ {fullText.length > 200 && ( + + )} +
+
+ ); + }) + ) : ( +
+
+
No reviews available.
-
- We had a delightful experience at Shivas Dosa. The service was exceptional, and the food was amazing.The Mysore Bonda and Chicken 65 reminded me of Bangalore, and the Mutton Chukka and Biryani were top-notch.A special mention for Rajatha's humble and attentive service. I truly appreciate their wonderful service and incredible food. Highly recommended! -
- Anoop V - {/* web designer */}
-
-
-
-
- {/* */} -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- I ordered two party trays of Ambur chicken biryani and one tray of chicken 65. The price was very reasonable compared to other restaurants in Kitchener and Cambridge. The taste was absolutely delicious, and the Ambur-style biryani was flavorful and authentic. The chicken 65 was crispy and well-seasoned. The service provided by Pungulali was excellent. Highly recommend this place for great food at a great price! -
- Ramesh Babu Gnana Kandan - {/* web designer */} -
-
-
-
-
- {/* */} -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- I had a great experience at Shiva Dosa Point today! I ordered Sambhar Idli, Chicken Kothu Parotta, and Coffee, and everything was delicious. The flavors were authentic, and the food was served fresh and hot. A special mention to Poonguzhali and Swetha for their excellent service—they were friendly, attentive, and made the dining experience even better. Definitely recommend this place for anyone craving good South Indian food! -
- Murali Krishna - {/* web designer */} -
-
-
-
-
- {/* */} -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- I had a wonderful experience at Shiva Dosa Restaurant! The food was absolutely delicious — authentic South Indian flavors served hot and fresh. We truly didn’t feel far from India. But what truly made our visit special was the outstanding service. A big thank you to Poonguzhali and Smriti, who were both incredibly attentive, kind, and professional throughout our meal. Their warm hospitality made us feel right at home. Highly recommend this place and their food. -
- Serah Benjamin - {/* web designer */} -
-
+ )} +
+ + Review us on Google + +
diff --git a/components/TestimonialAbout.js b/components/TestimonialAbout.js index 2e1298d..1691956 100644 --- a/components/TestimonialAbout.js +++ b/components/TestimonialAbout.js @@ -8,10 +8,64 @@ function TestimonialAbout() { const [nav2, setNav2] = useState(null); const [slider1, setSlider1] = useState(null); const [slider2, setSlider2] = useState(null); + const [reviews, setReviews] = useState([]); + const [loading, setLoading] = useState(true); + const [expandedReview, setExpandedReview] = useState(null); + useEffect(() => { setNav1(slider1); setNav2(slider2); - }); + }, [slider1, slider2]); + + 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("About: 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) => ( + + )); + } + + 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 null; + return url.startsWith("http") ? url : `https://lh3.googleusercontent.com/${url}`; + } + + function getInitials(name) { + if (!name) return "U"; + return name.split(' ').map(n => n[0]).join('').substring(0, 2).toUpperCase(); + } + + const link = "https://www.google.com/maps/place/Shivas+Dosa+Restaurant/@43.4056825,-80.5035997,17z/data=!4m8!3m7!1s0x882bf579045db13b:0x258e9b3ccf2549d4!8m2!3d43.4056825!4d-80.5010248!9m1!1b1!16s%2Fg%2F11tj6q0n1j?entry=ttu&g_ep=EgoyMDI1MTIwOS4wIKXMDSoASAFQAw%3D%3D"; const thumbs = { dots: false, @@ -92,43 +146,81 @@ function TestimonialAbout() { data-aos-duration={1500} data-aos-offset={50} > -
-
- The food was exceptional, with the dosas being a particular highlight and deserving of strong recommendation. Their taste was remarkable, representing the best dosas in the entire KWC region. The staff provided polite and attentive service. While the quantity of chutneys and sauces could be improved, the overall dining experience was highly satisfactory. + {loading ? ( +
+
Loading reviews...
- Mohit - {/* Manager */} -
-
-
- We had a delightful experience at Shivas Dosa. The service was exceptional, and the food was amazing.The Mysore Bonda and Chicken 65 reminded me of Bangalore, and the Mutton Chukka and Biryani were top-notch.A special mention for Rajatha's humble and attentive service. I truly appreciate their wonderful service and incredible food. Highly recommended! -
- Anoop V - {/* Manager */} -
-
-
- I ordered two party trays of Ambur chicken biryani and one tray of chicken 65. The price was very reasonable compared to other restaurants in Kitchener and Cambridge. The taste was absolutely delicious, and the Ambur-style biryani was flavorful and authentic. The chicken 65 was crispy and well-seasoned. The service provided by Pungulali was excellent. Highly recommend this place for great food at a great price! -
- Ramesh Babu Gnana Kandan - {/* Manager */} -
-
-
- I had a great experience at Shiva Dosa Point today! I ordered Sambhar Idli, Chicken Kothu Parotta, and Coffee, and everything was delicious. The flavors were authentic, and the food was served fresh and hot. A special mention to Poonguzhali and Swetha for their excellent service—they were friendly, attentive, and made the dining experience even better. Definitely recommend this place for anyone craving good South Indian food! -
- Murali Krishna - {/* Manager */} -
-
-
- I had a wonderful experience at Shiva Dosa Restaurant! The food was absolutely delicious — authentic South Indian flavors served hot and fresh. We truly didn’t feel far from India. But what truly made our visit special was the outstanding service. A big thank you to Poonguzhali and Smriti, who were both incredibly attentive, kind, and professional throughout our meal. Their warm hospitality made us feel right at home. Highly recommend this place and their food. -
- Serah Benjamin - {/* Manager */} -
+ ) : displayedReviews.length > 0 ? ( + 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 ( +
+
+
+ {profileImg ? ( + {name} (e.target.style.display = 'none')} + style={{ width: '100%', height: '100%', objectFit: 'cover' }} + /> + ) : ( + {getInitials(name)} + )} +
+
+

+ {name} +

+
+ {renderStars(r.rating)} +
+
+
+
+ {isExpanded ? fullText : truncateText(fullText)} +
+ {fullText.length > 200 && ( + + )} +
+ ); + }) + ) : ( +
+
No reviews available.
+
+ )} +
diff --git a/utility/sliderProps.js b/utility/sliderProps.js index 8b92ab1..3d388e3 100644 --- a/utility/sliderProps.js +++ b/utility/sliderProps.js @@ -110,13 +110,13 @@ export const sliderProps = { testimonialsTwoCarousel: { slidesToShow: 1, slidesToScroll: 1, - infinite: false, + infinite: true, speed: 400, arrows: false, dots: true, fade: true, focusOnSelect: true, - autoplay: false, + autoplay: true, autoplaySpeed: 5000, }, clientActive: { @@ -232,13 +232,13 @@ export const sliderProps = { testimonialsFiveContent: { slidesToShow: 1, slidesToScroll: 1, - infinite: false, + infinite: true, speed: 400, arrows: false, dots: true, fade: true, focusOnSelect: true, - autoplay: false, + autoplay: true, autoplaySpeed: 5000, }, };