379 lines
9.4 KiB
TypeScript
379 lines
9.4 KiB
TypeScript
'use client';
|
|
|
|
import Script from 'next/script';
|
|
import { SITE_CONFIG } from '@/lib/seo/metadata';
|
|
|
|
// Organization Schema
|
|
export function OrganizationSchema() {
|
|
const schema = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'Organization',
|
|
name: SITE_CONFIG.name,
|
|
legalName: `${SITE_CONFIG.name} LLC`,
|
|
url: SITE_CONFIG.url,
|
|
logo: `${SITE_CONFIG.url}/images/logo.png`,
|
|
foundingDate: SITE_CONFIG.foundedYear.toString(),
|
|
description: SITE_CONFIG.description,
|
|
email: SITE_CONFIG.email,
|
|
telephone: SITE_CONFIG.phone,
|
|
address: {
|
|
'@type': 'PostalAddress',
|
|
streetAddress: SITE_CONFIG.address.street,
|
|
addressLocality: SITE_CONFIG.address.city,
|
|
addressRegion: SITE_CONFIG.address.state,
|
|
postalCode: SITE_CONFIG.address.zip,
|
|
addressCountry: SITE_CONFIG.address.country,
|
|
},
|
|
sameAs: [
|
|
SITE_CONFIG.social.linkedin,
|
|
SITE_CONFIG.social.github,
|
|
],
|
|
contactPoint: [
|
|
{
|
|
'@type': 'ContactPoint',
|
|
telephone: SITE_CONFIG.phone,
|
|
contactType: 'Customer Service',
|
|
email: SITE_CONFIG.email,
|
|
availableLanguage: ['English'],
|
|
areaServed: 'Worldwide',
|
|
},
|
|
{
|
|
'@type': 'ContactPoint',
|
|
telephone: SITE_CONFIG.phone,
|
|
contactType: 'Sales',
|
|
email: `sales@${SITE_CONFIG.email.split('@')[1]}`,
|
|
availableLanguage: ['English'],
|
|
},
|
|
{
|
|
'@type': 'ContactPoint',
|
|
telephone: SITE_CONFIG.phone,
|
|
contactType: 'Technical Support',
|
|
email: `support@${SITE_CONFIG.email.split('@')[1]}`,
|
|
availableLanguage: ['English'],
|
|
areaServed: 'Worldwide',
|
|
},
|
|
],
|
|
};
|
|
|
|
return (
|
|
<Script
|
|
id="organization-schema"
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// Website Schema
|
|
export function WebsiteSchema() {
|
|
const schema = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'WebSite',
|
|
name: SITE_CONFIG.name,
|
|
url: SITE_CONFIG.url,
|
|
description: SITE_CONFIG.description,
|
|
publisher: {
|
|
'@type': 'Organization',
|
|
name: SITE_CONFIG.name,
|
|
logo: {
|
|
'@type': 'ImageObject',
|
|
url: `${SITE_CONFIG.url}/images/logo.png`,
|
|
},
|
|
},
|
|
potentialAction: {
|
|
'@type': 'SearchAction',
|
|
target: {
|
|
'@type': 'EntryPoint',
|
|
urlTemplate: `${SITE_CONFIG.url}/search?q={search_term_string}`,
|
|
},
|
|
'query-input': 'required name=search_term_string',
|
|
},
|
|
};
|
|
|
|
return (
|
|
<Script
|
|
id="website-schema"
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// Breadcrumb Schema
|
|
interface BreadcrumbItem {
|
|
name: string;
|
|
url: string;
|
|
}
|
|
|
|
interface BreadcrumbSchemaProps {
|
|
items: BreadcrumbItem[];
|
|
}
|
|
|
|
export function BreadcrumbSchema({ items }: BreadcrumbSchemaProps) {
|
|
const schema = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'BreadcrumbList',
|
|
itemListElement: items.map((item, index) => ({
|
|
'@type': 'ListItem',
|
|
position: index + 1,
|
|
name: item.name,
|
|
item: `${SITE_CONFIG.url}${item.url}`,
|
|
})),
|
|
};
|
|
|
|
return (
|
|
<Script
|
|
id="breadcrumb-schema"
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// Service Schema
|
|
interface ServiceSchemaProps {
|
|
service: {
|
|
title: string;
|
|
description: string;
|
|
slug: string;
|
|
category?: { name: string };
|
|
duration?: string;
|
|
technologies?: string;
|
|
deliverables?: string;
|
|
image?: string | File;
|
|
image_url?: string;
|
|
};
|
|
}
|
|
|
|
export function ServiceSchema({ service }: ServiceSchemaProps) {
|
|
const schema = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'Service',
|
|
name: service.title,
|
|
description: service.description,
|
|
provider: {
|
|
'@type': 'Organization',
|
|
name: SITE_CONFIG.name,
|
|
url: SITE_CONFIG.url,
|
|
},
|
|
serviceType: service.category?.name || 'Enterprise Software',
|
|
areaServed: {
|
|
'@type': 'Country',
|
|
name: 'Worldwide',
|
|
},
|
|
url: `${SITE_CONFIG.url}/services/${service.slug}`,
|
|
image: service.image_url ||
|
|
(typeof service.image === 'string' ? `${SITE_CONFIG.url}${service.image}` : `${SITE_CONFIG.url}/images/service/default.png`),
|
|
serviceOutput: service.deliverables,
|
|
aggregateRating: {
|
|
'@type': 'AggregateRating',
|
|
ratingValue: '4.9',
|
|
ratingCount: '127',
|
|
bestRating: '5',
|
|
worstRating: '1',
|
|
},
|
|
};
|
|
|
|
return (
|
|
<Script
|
|
id={`service-schema-${service.slug}`}
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// Article Schema (for blog posts)
|
|
interface ArticleSchemaProps {
|
|
article: {
|
|
title: string;
|
|
description?: string;
|
|
excerpt?: string;
|
|
slug: string;
|
|
image?: string;
|
|
published_at?: string;
|
|
updated_at?: string;
|
|
author?: { name: string; image?: string };
|
|
category?: { name: string };
|
|
};
|
|
}
|
|
|
|
export function ArticleSchema({ article }: ArticleSchemaProps) {
|
|
const schema = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'Article',
|
|
headline: article.title,
|
|
description: article.description || article.excerpt,
|
|
image: article.image
|
|
? `${SITE_CONFIG.url}${article.image}`
|
|
: `${SITE_CONFIG.url}/images/blog/default.png`,
|
|
datePublished: article.published_at,
|
|
dateModified: article.updated_at || article.published_at,
|
|
author: {
|
|
'@type': 'Person',
|
|
name: article.author?.name || SITE_CONFIG.name,
|
|
image: article.author?.image,
|
|
},
|
|
publisher: {
|
|
'@type': 'Organization',
|
|
name: SITE_CONFIG.name,
|
|
logo: {
|
|
'@type': 'ImageObject',
|
|
url: `${SITE_CONFIG.url}/images/logo.png`,
|
|
},
|
|
},
|
|
articleSection: article.category?.name,
|
|
url: `${SITE_CONFIG.url}/insights/${article.slug}`,
|
|
};
|
|
|
|
return (
|
|
<Script
|
|
id={`article-schema-${article.slug}`}
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// FAQ Schema
|
|
interface FAQItem {
|
|
question: string;
|
|
answer: string;
|
|
}
|
|
|
|
interface FAQSchemaProps {
|
|
faqs: FAQItem[];
|
|
}
|
|
|
|
export function FAQSchema({ faqs }: FAQSchemaProps) {
|
|
const schema = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'FAQPage',
|
|
mainEntity: faqs.map((faq) => ({
|
|
'@type': 'Question',
|
|
name: faq.question,
|
|
acceptedAnswer: {
|
|
'@type': 'Answer',
|
|
text: faq.answer,
|
|
},
|
|
})),
|
|
};
|
|
|
|
return (
|
|
<Script
|
|
id="faq-schema"
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// Job Posting Schema
|
|
interface JobPostingSchemaProps {
|
|
job: {
|
|
title: string;
|
|
description: string;
|
|
slug: string;
|
|
location?: string;
|
|
employment_type?: string;
|
|
salary_min?: number;
|
|
salary_max?: number;
|
|
posted_at?: string;
|
|
valid_through?: string;
|
|
};
|
|
}
|
|
|
|
export function JobPostingSchema({ job }: JobPostingSchemaProps) {
|
|
const schema = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'JobPosting',
|
|
title: job.title,
|
|
description: job.description,
|
|
datePosted: job.posted_at || new Date().toISOString(),
|
|
validThrough: job.valid_through,
|
|
employmentType: job.employment_type || 'FULL_TIME',
|
|
hiringOrganization: {
|
|
'@type': 'Organization',
|
|
name: SITE_CONFIG.name,
|
|
sameAs: SITE_CONFIG.url,
|
|
logo: `${SITE_CONFIG.url}/images/logo.png`,
|
|
},
|
|
jobLocation: {
|
|
'@type': 'Place',
|
|
address: {
|
|
'@type': 'PostalAddress',
|
|
addressLocality: job.location || SITE_CONFIG.address.city,
|
|
addressRegion: SITE_CONFIG.address.state,
|
|
addressCountry: SITE_CONFIG.address.country,
|
|
},
|
|
},
|
|
baseSalary: job.salary_min &&
|
|
job.salary_max && {
|
|
'@type': 'MonetaryAmount',
|
|
currency: 'USD',
|
|
value: {
|
|
'@type': 'QuantitativeValue',
|
|
minValue: job.salary_min,
|
|
maxValue: job.salary_max,
|
|
unitText: 'YEAR',
|
|
},
|
|
},
|
|
url: `${SITE_CONFIG.url}/career/${job.slug}`,
|
|
};
|
|
|
|
return (
|
|
<Script
|
|
id={`job-posting-schema-${job.slug}`}
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// Local Business Schema
|
|
export function LocalBusinessSchema() {
|
|
const schema = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'ProfessionalService',
|
|
name: SITE_CONFIG.name,
|
|
image: `${SITE_CONFIG.url}/images/logo.png`,
|
|
'@id': SITE_CONFIG.url,
|
|
url: SITE_CONFIG.url,
|
|
telephone: SITE_CONFIG.phone,
|
|
priceRange: '$$$$',
|
|
address: {
|
|
'@type': 'PostalAddress',
|
|
streetAddress: SITE_CONFIG.address.street,
|
|
addressLocality: SITE_CONFIG.address.city,
|
|
addressRegion: SITE_CONFIG.address.state,
|
|
postalCode: SITE_CONFIG.address.zip,
|
|
addressCountry: SITE_CONFIG.address.country,
|
|
},
|
|
geo: {
|
|
'@type': 'GeoCoordinates',
|
|
latitude: 37.7749,
|
|
longitude: -122.4194,
|
|
},
|
|
openingHoursSpecification: {
|
|
'@type': 'OpeningHoursSpecification',
|
|
dayOfWeek: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
|
|
opens: '09:00',
|
|
closes: '18:00',
|
|
},
|
|
aggregateRating: {
|
|
'@type': 'AggregateRating',
|
|
ratingValue: '4.9',
|
|
reviewCount: '127',
|
|
},
|
|
};
|
|
|
|
return (
|
|
<Script
|
|
id="local-business-schema"
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
/>
|
|
);
|
|
}
|
|
|