167 lines
3.6 KiB
TypeScript
167 lines
3.6 KiB
TypeScript
import type { Metadata } from "next";
|
|
import { siteConfig } from "@/data/site-content";
|
|
|
|
type PageMetadataInput = {
|
|
title: string;
|
|
description: string;
|
|
path: string;
|
|
image?: string;
|
|
};
|
|
|
|
type FaqEntry = {
|
|
question: string;
|
|
answer: string;
|
|
};
|
|
|
|
type ItemListEntry = {
|
|
name: string;
|
|
path?: string;
|
|
};
|
|
|
|
export function buildMetadataBase() {
|
|
const rawUrl = process.env.NEXT_PUBLIC_SITE_URL || siteConfig.siteUrl;
|
|
|
|
return new URL(rawUrl.endsWith("/") ? rawUrl : `${rawUrl}/`);
|
|
}
|
|
|
|
export function buildAbsoluteUrl(path: string) {
|
|
return new URL(path, buildMetadataBase()).toString();
|
|
}
|
|
|
|
export function buildPageMetadata({
|
|
title,
|
|
description,
|
|
path,
|
|
image = siteConfig.defaultOgImage,
|
|
}: PageMetadataInput): Metadata {
|
|
return {
|
|
title,
|
|
description,
|
|
alternates: {
|
|
canonical: path,
|
|
},
|
|
openGraph: {
|
|
title,
|
|
description,
|
|
url: path,
|
|
images: [
|
|
{
|
|
url: image,
|
|
alt: title,
|
|
},
|
|
],
|
|
},
|
|
twitter: {
|
|
card: "summary_large_image",
|
|
title,
|
|
description,
|
|
images: [image],
|
|
},
|
|
};
|
|
}
|
|
|
|
export function websiteSchema() {
|
|
return {
|
|
"@type": "WebSite",
|
|
name: siteConfig.name,
|
|
url: buildAbsoluteUrl("/"),
|
|
};
|
|
}
|
|
|
|
export function businessSchema() {
|
|
return {
|
|
"@type": "LocalBusiness",
|
|
name: siteConfig.name,
|
|
url: buildAbsoluteUrl("/"),
|
|
description: siteConfig.description,
|
|
logo: buildAbsoluteUrl(siteConfig.brand.primaryLogo),
|
|
image: buildAbsoluteUrl(siteConfig.defaultOgImage),
|
|
telephone: siteConfig.phoneDisplay,
|
|
address: {
|
|
"@type": "PostalAddress",
|
|
streetAddress: siteConfig.address.street,
|
|
addressLocality: "Corpus Christi",
|
|
addressRegion: "TX",
|
|
postalCode: "78405",
|
|
addressCountry: "US",
|
|
},
|
|
sameAs: siteConfig.socials.map((social) => social.href),
|
|
contactPoint: [
|
|
{
|
|
"@type": "ContactPoint",
|
|
telephone: siteConfig.phoneDisplay,
|
|
contactType: "customer service",
|
|
areaServed: "Corpus Christi, TX",
|
|
},
|
|
],
|
|
openingHoursSpecification: [
|
|
{
|
|
"@type": "OpeningHoursSpecification",
|
|
dayOfWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
|
|
opens: "08:00",
|
|
closes: "17:00",
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
export function localBusinessSchema() {
|
|
return {
|
|
"@context": "https://schema.org",
|
|
...businessSchema(),
|
|
};
|
|
}
|
|
|
|
export function breadcrumbSchema(items: { name: string; path: string }[]) {
|
|
return {
|
|
"@context": "https://schema.org",
|
|
"@type": "BreadcrumbList",
|
|
itemListElement: items.map((item, index) => ({
|
|
"@type": "ListItem",
|
|
position: index + 1,
|
|
name: item.name,
|
|
item: buildAbsoluteUrl(item.path),
|
|
})),
|
|
};
|
|
}
|
|
|
|
export function faqPageSchema(items: FaqEntry[]) {
|
|
return {
|
|
"@context": "https://schema.org",
|
|
"@type": "FAQPage",
|
|
mainEntity: items.map((item) => ({
|
|
"@type": "Question",
|
|
name: item.question,
|
|
acceptedAnswer: {
|
|
"@type": "Answer",
|
|
text: item.answer,
|
|
},
|
|
})),
|
|
};
|
|
}
|
|
|
|
export function itemListSchema({
|
|
name,
|
|
path,
|
|
items,
|
|
}: {
|
|
name: string;
|
|
path: string;
|
|
items: ItemListEntry[];
|
|
}) {
|
|
return {
|
|
"@context": "https://schema.org",
|
|
"@type": "ItemList",
|
|
name,
|
|
url: buildAbsoluteUrl(path),
|
|
numberOfItems: items.length,
|
|
itemListOrder: "https://schema.org/ItemListOrderAscending",
|
|
itemListElement: items.map((item, index) => ({
|
|
"@type": "ListItem",
|
|
position: index + 1,
|
|
name: item.name,
|
|
url: item.path ? buildAbsoluteUrl(item.path) : undefined,
|
|
})),
|
|
};
|
|
}
|