SEO/AEO
This commit is contained in:
187
src/utils/structuredData.ts
Normal file
187
src/utils/structuredData.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* Utility functions for generating structured data (JSON-LD) schemas
|
||||
* Enhances SEO and AEO by providing rich metadata to search engines
|
||||
*/
|
||||
|
||||
export interface ArticleSchemaProps {
|
||||
headline: string;
|
||||
description: string;
|
||||
author: string;
|
||||
datePublished: string;
|
||||
dateModified?: string;
|
||||
image?: string;
|
||||
url: string;
|
||||
keywords?: string[];
|
||||
articleBody?: string;
|
||||
}
|
||||
|
||||
export interface ServiceSchemaProps {
|
||||
name: string;
|
||||
description: string;
|
||||
provider: string;
|
||||
areaServed: string[];
|
||||
url: string;
|
||||
image?: string;
|
||||
offers?: {
|
||||
price?: string;
|
||||
priceCurrency?: string;
|
||||
availability?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ReviewSchemaProps {
|
||||
author: string;
|
||||
datePublished: string;
|
||||
reviewBody: string;
|
||||
ratingValue: number;
|
||||
bestRating?: number;
|
||||
worstRating?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Article/BlogPosting schema for blog posts
|
||||
*/
|
||||
export const generateArticleSchema = (props: ArticleSchemaProps) => {
|
||||
const schema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BlogPosting",
|
||||
headline: props.headline,
|
||||
description: props.description,
|
||||
author: {
|
||||
"@type": "Organization",
|
||||
name: props.author,
|
||||
url: "https://bayarea-cc.com"
|
||||
},
|
||||
publisher: {
|
||||
"@type": "Organization",
|
||||
name: "Bay Area Affiliates",
|
||||
logo: {
|
||||
"@type": "ImageObject",
|
||||
url: "https://bayarea-cc.com/logo_bayarea.svg"
|
||||
}
|
||||
},
|
||||
datePublished: props.datePublished,
|
||||
dateModified: props.dateModified || props.datePublished,
|
||||
mainEntityOfPage: {
|
||||
"@type": "WebPage",
|
||||
"@id": props.url
|
||||
},
|
||||
...(props.image && {
|
||||
image: {
|
||||
"@type": "ImageObject",
|
||||
url: props.image
|
||||
}
|
||||
}),
|
||||
...(props.keywords && { keywords: props.keywords.join(", ") }),
|
||||
...(props.articleBody && { articleBody: props.articleBody })
|
||||
};
|
||||
|
||||
return schema;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate Service schema for service pages
|
||||
*/
|
||||
export const generateServiceSchema = (props: ServiceSchemaProps) => {
|
||||
const schema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Service",
|
||||
name: props.name,
|
||||
description: props.description,
|
||||
provider: {
|
||||
"@type": "Organization",
|
||||
name: props.provider,
|
||||
url: "https://bayarea-cc.com"
|
||||
},
|
||||
areaServed: props.areaServed.map(area => ({
|
||||
"@type": "City",
|
||||
name: area
|
||||
})),
|
||||
url: props.url,
|
||||
...(props.image && {
|
||||
image: {
|
||||
"@type": "ImageObject",
|
||||
url: props.image
|
||||
}
|
||||
}),
|
||||
...(props.offers && {
|
||||
offers: {
|
||||
"@type": "Offer",
|
||||
...props.offers,
|
||||
seller: {
|
||||
"@type": "Organization",
|
||||
name: props.provider
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return schema;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate Review schema
|
||||
*/
|
||||
export const generateReviewSchema = (props: ReviewSchemaProps) => {
|
||||
const schema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Review",
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: props.author
|
||||
},
|
||||
datePublished: props.datePublished,
|
||||
reviewBody: props.reviewBody,
|
||||
reviewRating: {
|
||||
"@type": "Rating",
|
||||
ratingValue: props.ratingValue,
|
||||
bestRating: props.bestRating || 5,
|
||||
worstRating: props.worstRating || 1
|
||||
},
|
||||
itemReviewed: {
|
||||
"@type": "LocalBusiness",
|
||||
name: "Bay Area Affiliates",
|
||||
url: "https://bayarea-cc.com"
|
||||
}
|
||||
};
|
||||
|
||||
return schema;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate FAQ schema
|
||||
*/
|
||||
export const generateFAQSchema = (faqs: Array<{ question: string; answer: string }>) => {
|
||||
const schema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "FAQPage",
|
||||
mainEntity: faqs.map(faq => ({
|
||||
"@type": "Question",
|
||||
name: faq.question,
|
||||
acceptedAnswer: {
|
||||
"@type": "Answer",
|
||||
text: faq.answer
|
||||
}
|
||||
}))
|
||||
};
|
||||
|
||||
return schema;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject structured data into the page
|
||||
*/
|
||||
export const injectStructuredData = (schema: object, id: string = "structured-data") => {
|
||||
// Remove existing script if present
|
||||
const existing = document.getElementById(id);
|
||||
if (existing) {
|
||||
existing.remove();
|
||||
}
|
||||
|
||||
// Create and inject new script
|
||||
const script = document.createElement("script");
|
||||
script.id = id;
|
||||
script.type = "application/ld+json";
|
||||
script.text = JSON.stringify(schema);
|
||||
document.head.appendChild(script);
|
||||
};
|
||||
Reference in New Issue
Block a user