87 lines
2.2 KiB
TypeScript
87 lines
2.2 KiB
TypeScript
import { businessConfig } from "@/lib/seo-config"
|
|
|
|
interface Review {
|
|
author?: string
|
|
rating?: number
|
|
reviewBody?: string
|
|
datePublished?: string
|
|
}
|
|
|
|
interface ReviewSchemaProps {
|
|
aggregateRating?: {
|
|
ratingValue: number
|
|
reviewCount: number
|
|
bestRating?: number
|
|
worstRating?: number
|
|
}
|
|
reviews?: Review[]
|
|
}
|
|
|
|
/**
|
|
* Review Schema Component
|
|
* Implements AggregateRating and Review schema markup for SEO
|
|
* Based on review data from reputation hub and Google reviews
|
|
*/
|
|
export function ReviewSchema({ aggregateRating, reviews }: ReviewSchemaProps) {
|
|
if (!aggregateRating && (!reviews || reviews.length === 0)) {
|
|
return null
|
|
}
|
|
|
|
const structuredData: any = {
|
|
"@context": "https://schema.org",
|
|
"@type": "AggregateRating",
|
|
}
|
|
|
|
if (aggregateRating) {
|
|
structuredData.ratingValue = aggregateRating.ratingValue
|
|
structuredData.reviewCount = aggregateRating.reviewCount
|
|
structuredData.bestRating = aggregateRating.bestRating || 5
|
|
structuredData.worstRating = aggregateRating.worstRating || 1
|
|
}
|
|
|
|
// If we have individual reviews, create a full Review schema
|
|
if (reviews && reviews.length > 0) {
|
|
const reviewItems = reviews.map((review) => ({
|
|
"@type": "Review",
|
|
author: {
|
|
"@type": "Person",
|
|
name: review.author || "Customer",
|
|
},
|
|
reviewRating: review.rating
|
|
? {
|
|
"@type": "Rating",
|
|
ratingValue: review.rating,
|
|
bestRating: 5,
|
|
worstRating: 1,
|
|
}
|
|
: undefined,
|
|
reviewBody: review.reviewBody,
|
|
datePublished: review.datePublished,
|
|
}))
|
|
|
|
return (
|
|
<>
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{
|
|
__html: JSON.stringify({
|
|
"@context": "https://schema.org",
|
|
"@type": "LocalBusiness",
|
|
name: businessConfig.name,
|
|
aggregateRating: structuredData,
|
|
review: reviewItems,
|
|
}),
|
|
}}
|
|
/>
|
|
</>
|
|
)
|
|
}
|
|
|
|
// Just aggregate rating
|
|
return (
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
|
|
/>
|
|
)
|
|
}
|