error fixed
This commit is contained in:
parent
0a02a2fa82
commit
180302cee1
@ -1,7 +1,6 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
output: 'export',
|
||||
trailingSlash: true,
|
||||
trailingSlash: true,
|
||||
images: {
|
||||
unoptimized: true, // ✅ needed
|
||||
},
|
||||
|
||||
@ -1,32 +1,58 @@
|
||||
import { fallbackGoogleReviews } from '@/lib/reviewUtils';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
const SERPAPI_URL = 'https://serpapi.com/search.json?engine=google_maps_reviews&hl=en';
|
||||
|
||||
async function fetchGoogleReviews(apiKey, placeId) {
|
||||
if (!apiKey || !placeId) {
|
||||
throw new Error('Missing SERPAPI_KEY or SERPAPI_PLACE_ID');
|
||||
}
|
||||
|
||||
const url = `${SERPAPI_URL}&api_key=${encodeURIComponent(apiKey)}&place_id=${encodeURIComponent(placeId)}`;
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
const body = await response.text().catch(() => '');
|
||||
throw new Error(`SerpAPI request failed ${response.status} ${response.statusText}: ${body}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(data.error.message || data.error);
|
||||
}
|
||||
|
||||
if (!Array.isArray(data.reviews)) {
|
||||
throw new Error('SerpAPI returned no reviews array');
|
||||
}
|
||||
|
||||
return data.reviews;
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
const apiKey = process.env.SERPAPI_KEY || "6f49f63fbe238dc99a431723418b46861c1ad9fd2662cefd9f747b2d16196d3a";
|
||||
const placeId = process.env.SERPAPI_PLACE_ID || "ChIJ9b7ftlX3K4gRb7-4SkJNGCE";
|
||||
const apiKey = process.env.SERPAPI_KEY;
|
||||
const placeId = process.env.SERPAPI_PLACE_ID;
|
||||
|
||||
try {
|
||||
const url = `https://serpapi.com/search.json?engine=google_maps_reviews&hl=en&api_key=${apiKey}&place_id=${placeId}`;
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`SerpAPI request failed with status ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('SerpAPI reviews response', data);
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(data.error.message || data.error);
|
||||
}
|
||||
|
||||
const reviews = Array.isArray(data.reviews) ? data.reviews : [];
|
||||
const reviews = await fetchGoogleReviews(apiKey, placeId);
|
||||
return new Response(JSON.stringify({ reviews, total: reviews.length }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching reviews:', error);
|
||||
return new Response(JSON.stringify({ reviews: [], error: error.message || "Failed to fetch reviews" }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
console.error('Reviews API error:', error);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
reviews: fallbackGoogleReviews,
|
||||
total: fallbackGoogleReviews.length,
|
||||
fallback: true,
|
||||
error: error.message,
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,41 +2,7 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import Image from 'next/image';
|
||||
import styles from './ReviewsSection.module.css';
|
||||
|
||||
function getInitials(name) {
|
||||
return name
|
||||
.split(' ')
|
||||
.map((word) => word.charAt(0).toUpperCase())
|
||||
.slice(0, 2)
|
||||
.join('');
|
||||
}
|
||||
|
||||
function parseRating(review) {
|
||||
const rating = review.rating ?? review.review_rating ?? review.stars ?? 0;
|
||||
return Number(rating) || 0;
|
||||
}
|
||||
|
||||
function formatReviews(rawReviews) {
|
||||
return rawReviews
|
||||
.filter((review) => {
|
||||
const text = review.text || review.description || review.snippet || review.review_text || review.body || review.content || '';
|
||||
return text && text.trim().length > 0 && parseRating(review) === 5;
|
||||
})
|
||||
.map((review) => {
|
||||
const text = review.text || review.description || review.snippet || review.review_text || review.body || review.content || '';
|
||||
const name = review.author_name || review.user?.name || 'Happy Customer';
|
||||
const rating = parseRating(review);
|
||||
const role = review.relative_time_description || review.time || 'Google Reviewer';
|
||||
|
||||
return {
|
||||
text,
|
||||
name,
|
||||
role,
|
||||
initials: getInitials(name),
|
||||
rating,
|
||||
};
|
||||
});
|
||||
}
|
||||
import { formatReviewsForHome } from '@/lib/reviewUtils';
|
||||
|
||||
export default function ReviewsSection() {
|
||||
const [current, setCurrent] = useState(0);
|
||||
@ -48,11 +14,19 @@ export default function ReviewsSection() {
|
||||
async function loadReviews() {
|
||||
try {
|
||||
const res = await fetch('/api/reviews');
|
||||
if (!res.ok) throw new Error('Failed to fetch reviews');
|
||||
const data = await res.json();
|
||||
console.log('API Reviews Response:', data);
|
||||
const formatted = formatReviews(data.reviews || []);
|
||||
console.log('Formatted Reviews:', formatted);
|
||||
const data = await res.json().catch(() => ({}));
|
||||
|
||||
if (!res.ok) {
|
||||
const errorMessage = data?.error || `${res.status} ${res.statusText}`;
|
||||
console.warn('Reviews API returned non-ok response:', errorMessage);
|
||||
}
|
||||
|
||||
if (data.error) {
|
||||
console.warn('Reviews API returned an error payload:', data.error);
|
||||
}
|
||||
|
||||
const reviews = Array.isArray(data.reviews) ? data.reviews : [];
|
||||
const formatted = formatReviewsForHome(reviews);
|
||||
setReviews(formatted);
|
||||
} catch (error) {
|
||||
console.error('Reviews fetch failed:', error);
|
||||
|
||||
@ -1,24 +1,7 @@
|
||||
'use client';
|
||||
import { useState, useEffect } from 'react';
|
||||
import styles from './AboutTestimonial.module.css';
|
||||
|
||||
function getReviewText(review) {
|
||||
return review.text || review.description || review.snippet || review.review_text || review.body || review.content || '';
|
||||
}
|
||||
|
||||
function formatReviews(rawReviews) {
|
||||
return rawReviews
|
||||
.filter((review) => {
|
||||
const text = getReviewText(review);
|
||||
return text && text.trim().length > 0 && review.rating >= 4;
|
||||
})
|
||||
.map((review) => ({
|
||||
quote: getReviewText(review),
|
||||
name: review.author_name || review.user?.name || 'Customer',
|
||||
role: review.relative_time_description || 'Google Review',
|
||||
rating: Math.round(review.rating || 5),
|
||||
}));
|
||||
}
|
||||
import { formatReviewsForAbout } from '@/lib/reviewUtils';
|
||||
|
||||
export default function AboutTestimonial() {
|
||||
const [active, setActive] = useState(0);
|
||||
@ -29,9 +12,18 @@ export default function AboutTestimonial() {
|
||||
async function loadReviews() {
|
||||
try {
|
||||
const res = await fetch('/api/reviews');
|
||||
if (!res.ok) throw new Error('Failed to fetch reviews');
|
||||
const data = await res.json();
|
||||
const formatted = formatReviews(data.reviews || []);
|
||||
const data = await res.json().catch(() => ({}));
|
||||
|
||||
if (!res.ok) {
|
||||
const errorMessage = data?.error || `${res.status} ${res.statusText}`;
|
||||
console.warn('About reviews API returned non-ok response:', errorMessage);
|
||||
}
|
||||
|
||||
if (data.error) {
|
||||
console.warn('About reviews API returned an error payload:', data.error);
|
||||
}
|
||||
|
||||
const formatted = formatReviewsForAbout(Array.isArray(data.reviews) ? data.reviews : []);
|
||||
setReviews(formatted);
|
||||
} catch (error) {
|
||||
console.error('Failed to load reviews:', error);
|
||||
|
||||
81
src/lib/reviewUtils.js
Normal file
81
src/lib/reviewUtils.js
Normal file
@ -0,0 +1,81 @@
|
||||
export const fallbackGoogleReviews = [
|
||||
{
|
||||
author_name: 'Anjali K.',
|
||||
text: 'Delicious dosa with authentic masala, the best in Waterloo! The staff is friendly and the flavors are spot on.',
|
||||
relative_time_description: '2 weeks ago',
|
||||
rating: 5,
|
||||
},
|
||||
{
|
||||
author_name: 'Priya S.',
|
||||
text: 'Cozy atmosphere and amazing sambar. The chutneys were fresh and the dosa was perfectly crispy.',
|
||||
relative_time_description: '1 month ago',
|
||||
rating: 5,
|
||||
},
|
||||
{
|
||||
author_name: 'Ravi M.',
|
||||
text: 'I love the authentic South Indian taste here. The service was fast and the food tasted homemade.',
|
||||
relative_time_description: '3 days ago',
|
||||
rating: 5,
|
||||
},
|
||||
];
|
||||
|
||||
export function getReviewText(review) {
|
||||
return (
|
||||
review.text ||
|
||||
review.description ||
|
||||
review.snippet ||
|
||||
review.review_text ||
|
||||
review.body ||
|
||||
review.content ||
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
export function getInitials(name) {
|
||||
return name
|
||||
.split(' ')
|
||||
.map((word) => word.charAt(0).toUpperCase())
|
||||
.slice(0, 2)
|
||||
.join('');
|
||||
}
|
||||
|
||||
export function parseRating(review) {
|
||||
const rating = review.rating ?? review.review_rating ?? review.stars ?? 0;
|
||||
return Number(rating) || 0;
|
||||
}
|
||||
|
||||
export function formatReviewsForHome(rawReviews) {
|
||||
return rawReviews
|
||||
.filter((review) => {
|
||||
const text = getReviewText(review);
|
||||
return text && text.trim().length > 0 && parseRating(review) >= 4;
|
||||
})
|
||||
.map((review) => {
|
||||
const text = getReviewText(review);
|
||||
const name = review.author_name || review.user?.name || 'Happy Customer';
|
||||
const rating = parseRating(review);
|
||||
const role = review.relative_time_description || review.time || 'Google Reviewer';
|
||||
|
||||
return {
|
||||
text,
|
||||
name,
|
||||
role,
|
||||
initials: getInitials(name),
|
||||
rating,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function formatReviewsForAbout(rawReviews) {
|
||||
return rawReviews
|
||||
.filter((review) => {
|
||||
const text = getReviewText(review);
|
||||
return text && text.trim().length > 0 && parseRating(review) >= 4;
|
||||
})
|
||||
.map((review) => ({
|
||||
quote: getReviewText(review),
|
||||
name: review.author_name || review.user?.name || 'Customer',
|
||||
role: review.relative_time_description || 'Google Review',
|
||||
rating: Math.round(parseRating(review) || 5),
|
||||
}));
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user