feat(seo): LocalBusiness + CollectionPage structured data schemas
- Add localBusinessSchema() with geo coords, phone, opening hours for Google Local Pack and Knowledge Panel visibility - Add collectionPageSchema() with ItemList for article listing pages - Inject LocalBusiness alongside Organization+WebSite in root layout - Inject CollectionPage in /news hub page with article items Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,7 @@ export function generateStaticParams() {
|
||||
return routing.locales.map((locale) => ({ locale }));
|
||||
}
|
||||
import { getBranding, getSocialLinks } from '@/lib/siteSettings';
|
||||
import { organizationSchema, websiteSchema } from '@/lib/seo';
|
||||
import { organizationSchema, websiteSchema, localBusinessSchema } from '@/lib/seo';
|
||||
import JsonLd from '@/components/seo/JsonLd';
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
@@ -154,6 +154,10 @@ export default async function RootLayout({
|
||||
logoUrl: branding.logoUrl,
|
||||
sameAs: sameAs.length ? sameAs : undefined,
|
||||
}),
|
||||
localBusinessSchema({
|
||||
logoUrl: branding.logoUrl,
|
||||
sameAs: sameAs.length ? sameAs : undefined,
|
||||
}),
|
||||
websiteSchema(),
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -7,7 +7,8 @@ import BreathingField from "@/components/visuals/BreathingField";
|
||||
|
||||
import { getLocalizedData } from "@/lib/i18nHelper";
|
||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||
import { buildPageMetadata } from "@/lib/seo";
|
||||
import { buildPageMetadata, collectionPageSchema, baseUrl } from "@/lib/seo";
|
||||
import JsonLd from "@/components/seo/JsonLd";
|
||||
|
||||
// ISR: revalidate every 60s — see /[locale]/page.tsx for the full rationale.
|
||||
export const revalidate = 60;
|
||||
@@ -49,8 +50,22 @@ export default async function NewsHub({ params }: { params: Promise<{ locale: st
|
||||
const heroArticle = articles.length > 0 ? articles[0] : null;
|
||||
const gridArticles = articles.length > 1 ? articles.slice(1) : [];
|
||||
|
||||
const collectionSchema = articles.length > 0
|
||||
? collectionPageSchema({
|
||||
name: `${t("title1")} ${t("title2")} — FLUX`,
|
||||
description: t("description"),
|
||||
url: `${baseUrl()}/${locale}/news`,
|
||||
items: articles.map((a: any, idx: number) => ({
|
||||
name: a.title,
|
||||
url: `${baseUrl()}/${locale}/news/${a.slug}`,
|
||||
position: idx + 1,
|
||||
})),
|
||||
})
|
||||
: null;
|
||||
|
||||
return (
|
||||
<main className="relative min-h-screen pt-32 pb-24">
|
||||
{collectionSchema && <JsonLd data={collectionSchema} />}
|
||||
<BreathingField />
|
||||
|
||||
<div className="relative z-10 max-w-7xl mx-auto px-6">
|
||||
|
||||
@@ -114,6 +114,70 @@ export function organizationSchema(opts?: { logoUrl?: string; sameAs?: string[]
|
||||
};
|
||||
}
|
||||
|
||||
export function localBusinessSchema(opts?: { logoUrl?: string; sameAs?: string[] }) {
|
||||
const base = baseUrl();
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "LocalBusiness",
|
||||
"@id": `${base}/#local-business`,
|
||||
name: "FLUX Srl",
|
||||
legalName: "FLUX Srl",
|
||||
url: base,
|
||||
logo: opts?.logoUrl ? absoluteUrl(opts.logoUrl) : `${base}/flux-logo.png`,
|
||||
description:
|
||||
"Manufacturer of solid-state Radio Frequency (RF), Microwave, and Infrared industrial equipment since 1978.",
|
||||
foundingDate: "1978",
|
||||
founder: { "@type": "Person", name: "Patrizio Grando" },
|
||||
address: {
|
||||
"@type": "PostalAddress",
|
||||
streetAddress: "Via Benedetto Marcello 32",
|
||||
addressLocality: "Romano d'Ezzelino",
|
||||
addressRegion: "Vicenza",
|
||||
postalCode: "36060",
|
||||
addressCountry: "IT",
|
||||
},
|
||||
geo: {
|
||||
"@type": "GeoCoordinates",
|
||||
latitude: 45.7836,
|
||||
longitude: 11.7677,
|
||||
},
|
||||
telephone: "+39 0424 287 492",
|
||||
email: "info@rf-flux.com",
|
||||
openingHoursSpecification: {
|
||||
"@type": "OpeningHoursSpecification",
|
||||
dayOfWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
|
||||
opens: "08:00",
|
||||
closes: "17:00",
|
||||
},
|
||||
areaServed: { "@type": "Place", name: "Worldwide" },
|
||||
...(opts?.sameAs ? { sameAs: opts.sameAs } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function collectionPageSchema(opts: {
|
||||
name: string;
|
||||
description: string;
|
||||
url: string;
|
||||
items: { name: string; url: string; position: number }[];
|
||||
}) {
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "CollectionPage",
|
||||
name: opts.name,
|
||||
description: opts.description,
|
||||
url: opts.url,
|
||||
mainEntity: {
|
||||
"@type": "ItemList",
|
||||
itemListElement: opts.items.map((item) => ({
|
||||
"@type": "ListItem",
|
||||
position: item.position,
|
||||
url: item.url,
|
||||
name: item.name,
|
||||
})),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function websiteSchema() {
|
||||
const base = baseUrl();
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user