This commit is contained in:
Vula Builder
2026-10-11 14:20:23 +00:00
parent c95c365716
commit 087622e394
+67
View File
@@ -0,0 +1,67 @@
interface Profile {
businessName: string | null
schemaType: string
phone: string | null
email: string | null
socials?: { network: string; url: string }[]
address: {
street: string | null
locality: string | null
region: string | null
postalCode: string | null
country: string | null
} | null
}
/**
* schema.org LocalBusiness data for this business, rendered into the page.
*
* Server-side, and revalidated hourly rather than per request: this is the
* same answer for every visitor, and a crawler should not be waiting on our
* backend to see a page.
*/
export default async function BusinessSchema() {
const base = process.env.NEXT_PUBLIC_VULA_COMMERCE_URL
const id = process.env.NEXT_PUBLIC_VULA_PROJECT_ID
if (!base || !id) return null
let p: Profile | null = null
try {
const res = await fetch(base + '/api/public/profile/' + id, { next: { revalidate: 3600 } })
if (res.ok) p = (await res.json())?.profile ?? null
} catch {
// A page without structured data still renders. A page that fails to
// render because our backend is slow does not.
return null
}
if (!p?.businessName) return null
const data: Record<string, unknown> = {
'@context': 'https://schema.org',
'@type': p.schemaType || 'LocalBusiness',
name: p.businessName,
}
if (p.phone) data.telephone = p.phone
if (p.email) data.email = p.email
// sameAs is how Google ties this site to her Facebook and Instagram pages —
// the same entity match that decides whether she appears in the map listing.
if (p.socials?.length) data.sameAs = p.socials.map((s) => s.url)
if (p.address) {
data.address = {
'@type': 'PostalAddress',
streetAddress: p.address.street,
addressLocality: p.address.locality,
...(p.address.region ? { addressRegion: p.address.region } : {}),
...(p.address.postalCode ? { postalCode: p.address.postalCode } : {}),
addressCountry: p.address.country || 'ZA',
}
}
return (
<script
type="application/ld+json"
// The value is JSON we built ourselves from her own fields, never markup.
dangerouslySetInnerHTML={{ __html: JSON.stringify(data).replace(/</g, '\\u003c') }}
/>
)
}