87 lines
2.7 KiB
TypeScript
87 lines
2.7 KiB
TypeScript
type MediaLike = { url?: null | string } | null | undefined
|
|
|
|
type Address = {
|
|
city?: string
|
|
country?: string // ISO code, e.g. 'PL'
|
|
postalCode?: string
|
|
region?: string
|
|
street?: string
|
|
}
|
|
|
|
type LocalBusinessJsonLdArgs = {
|
|
address?: Address
|
|
/** Geo coordinates for maps/local search. */
|
|
geo?: { latitude: number; longitude: number }
|
|
image?: MediaLike
|
|
logo?: MediaLike
|
|
name: string
|
|
/** Opening hours, e.g. ['Mo-Fr 08:00-18:00', 'Sa 09:00-14:00']. */
|
|
openingHours?: string[]
|
|
priceRange?: string // e.g. '$$'
|
|
sameAs?: string[]
|
|
/** Business phone, e.g. '+48 123 456 789'. */
|
|
telephone?: string
|
|
url: string
|
|
}
|
|
|
|
/**
|
|
* Builds LocalBusiness JSON-LD (schema.org) — the key structured data for LOCAL
|
|
* SEO. Helps Google show the business in local results / map pack with address,
|
|
* hours, phone. Strong signal for "usługa + miasto" queries.
|
|
*
|
|
* All data from the panel (company global) — nothing hardcoded. Emit once in the
|
|
* root layout (business is site-wide):
|
|
*
|
|
* import { buildLocalBusinessJsonLd } from '@intecion/ipal-kit'
|
|
* const jsonLd = buildLocalBusinessJsonLd({
|
|
* name: company.name, url: baseUrl, telephone: company.phone,
|
|
* address: company.address, openingHours: company.hours,
|
|
* })
|
|
*
|
|
* For a more specific type (e.g. 'Dentist', 'Plumber'), override @type in the
|
|
* returned object — schema.org has many LocalBusiness subtypes.
|
|
*/
|
|
export function buildLocalBusinessJsonLd({
|
|
name,
|
|
address,
|
|
geo,
|
|
image,
|
|
logo,
|
|
openingHours,
|
|
priceRange,
|
|
sameAs,
|
|
telephone,
|
|
url,
|
|
}: LocalBusinessJsonLdArgs) {
|
|
const logoUrl = logo?.url
|
|
const imageUrl = image?.url ?? logoUrl
|
|
|
|
return {
|
|
name,
|
|
'@context': 'https://schema.org',
|
|
'@type': 'LocalBusiness',
|
|
url,
|
|
...(telephone ? { telephone } : {}),
|
|
...(imageUrl ? { image: imageUrl.startsWith('http') ? imageUrl : `${url}${imageUrl}` } : {}),
|
|
...(logoUrl ? { logo: logoUrl.startsWith('http') ? logoUrl : `${url}${logoUrl}` } : {}),
|
|
...(address
|
|
? {
|
|
address: {
|
|
'@type': 'PostalAddress',
|
|
...(address.street ? { streetAddress: address.street } : {}),
|
|
...(address.city ? { addressLocality: address.city } : {}),
|
|
...(address.postalCode ? { postalCode: address.postalCode } : {}),
|
|
...(address.region ? { addressRegion: address.region } : {}),
|
|
...(address.country ? { addressCountry: address.country } : {}),
|
|
},
|
|
}
|
|
: {}),
|
|
...(geo
|
|
? { geo: { '@type': 'GeoCoordinates', latitude: geo.latitude, longitude: geo.longitude } }
|
|
: {}),
|
|
...(openingHours && openingHours.length > 0 ? { openingHours } : {}),
|
|
...(priceRange ? { priceRange } : {}),
|
|
...(sameAs && sameAs.length > 0 ? { sameAs } : {}),
|
|
}
|
|
}
|