auto-claude: subtask-3-1 - Delete old JsonLd.tsx file
This commit is contained in:
+5
-5
@@ -3,23 +3,23 @@
|
|||||||
"spec": "032-split-large-jsonld-tsx-component-759-lines-into-se",
|
"spec": "032-split-large-jsonld-tsx-component-759-lines-into-se",
|
||||||
"state": "building",
|
"state": "building",
|
||||||
"subtasks": {
|
"subtasks": {
|
||||||
"completed": 15,
|
"completed": 16,
|
||||||
"total": 19,
|
"total": 19,
|
||||||
"in_progress": 1,
|
"in_progress": 1,
|
||||||
"failed": 0
|
"failed": 0
|
||||||
},
|
},
|
||||||
"phase": {
|
"phase": {
|
||||||
"current": "Migrate Imports",
|
"current": "Remove Old File",
|
||||||
"id": null,
|
"id": null,
|
||||||
"total": 2
|
"total": 1
|
||||||
},
|
},
|
||||||
"workers": {
|
"workers": {
|
||||||
"active": 0,
|
"active": 0,
|
||||||
"max": 1
|
"max": 1
|
||||||
},
|
},
|
||||||
"session": {
|
"session": {
|
||||||
"number": 3,
|
"number": 4,
|
||||||
"started_at": "2026-01-25T11:52:09.599134"
|
"started_at": "2026-01-25T11:52:09.599134"
|
||||||
},
|
},
|
||||||
"last_update": "2026-01-25T11:54:43.072497"
|
"last_update": "2026-01-25T11:58:46.607945"
|
||||||
}
|
}
|
||||||
@@ -1,758 +0,0 @@
|
|||||||
import { type Locale } from '@/i18n/config';
|
|
||||||
|
|
||||||
interface JsonLdProps {
|
|
||||||
locale: Locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function PersonJsonLd({ locale }: JsonLdProps) {
|
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
|
|
||||||
|
|
||||||
const schema = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'Person',
|
|
||||||
'@id': `${baseUrl}/#person`,
|
|
||||||
name: 'Damjan Savić',
|
|
||||||
givenName: 'Damjan',
|
|
||||||
familyName: 'Savić',
|
|
||||||
jobTitle: locale === 'de'
|
|
||||||
? 'AI & Automation Specialist | Fullstack Entwickler'
|
|
||||||
: locale === 'sr'
|
|
||||||
? 'AI & Automation Specialist | Fullstack Developer'
|
|
||||||
: 'AI & Automation Specialist | Fullstack Developer',
|
|
||||||
description: locale === 'de'
|
|
||||||
? 'Entwicklung von KI-Agenten und Automatisierungslösungen. Von Voice-AI-Plattformen bis zu autonomen Web-Agenten.'
|
|
||||||
: locale === 'sr'
|
|
||||||
? 'Razvoj AI agenata i rešenja za automatizaciju. Od voice AI platformi do autonomnih web agenata.'
|
|
||||||
: 'Remote AI developer building AI agents and automation solutions for clients in USA, UK and Europe. From voice AI platforms to autonomous web agents.',
|
|
||||||
url: baseUrl,
|
|
||||||
image: `${baseUrl}/images/og-image.jpg`,
|
|
||||||
email: 'info@damjan-savic.com',
|
|
||||||
telephone: '+491756950979',
|
|
||||||
address: {
|
|
||||||
'@type': 'PostalAddress',
|
|
||||||
addressLocality: locale === 'sr' ? 'Keln' : locale === 'en' ? 'Cologne' : 'Köln',
|
|
||||||
addressRegion: 'NRW',
|
|
||||||
addressCountry: 'DE',
|
|
||||||
},
|
|
||||||
sameAs: [
|
|
||||||
'https://www.linkedin.com/in/damjansavic/',
|
|
||||||
'https://github.com/damjansavic',
|
|
||||||
],
|
|
||||||
knowsAbout: [
|
|
||||||
'Artificial Intelligence',
|
|
||||||
'Machine Learning',
|
|
||||||
'Process Automation',
|
|
||||||
'Voice AI',
|
|
||||||
'Web Development',
|
|
||||||
'Python',
|
|
||||||
'TypeScript',
|
|
||||||
'React',
|
|
||||||
'Next.js',
|
|
||||||
'n8n',
|
|
||||||
'SaaS Development',
|
|
||||||
],
|
|
||||||
alumniOf: [
|
|
||||||
{
|
|
||||||
'@type': 'EducationalOrganization',
|
|
||||||
name: 'FOM Hochschule für Ökonomie & Management',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
hasCredential: [
|
|
||||||
{
|
|
||||||
'@type': 'EducationalOccupationalCredential',
|
|
||||||
name: 'M.A. Software Development',
|
|
||||||
credentialCategory: 'degree',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'EducationalOccupationalCredential',
|
|
||||||
name: 'B.Sc. Wirtschaftsinformatik',
|
|
||||||
credentialCategory: 'degree',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ProfessionalServiceJsonLd({ locale }: JsonLdProps) {
|
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
|
|
||||||
|
|
||||||
const serviceNames = {
|
|
||||||
de: [
|
|
||||||
'KI-Entwicklung',
|
|
||||||
'Prozessautomatisierung',
|
|
||||||
'Voice AI Entwicklung',
|
|
||||||
'SaaS Entwicklung',
|
|
||||||
'Fullstack Webentwicklung',
|
|
||||||
'n8n Automatisierung',
|
|
||||||
],
|
|
||||||
en: [
|
|
||||||
'AI Development',
|
|
||||||
'Process Automation',
|
|
||||||
'Voice AI Development',
|
|
||||||
'SaaS Development',
|
|
||||||
'Fullstack Web Development',
|
|
||||||
'n8n Automation',
|
|
||||||
],
|
|
||||||
sr: [
|
|
||||||
'AI Razvoj',
|
|
||||||
'Automatizacija procesa',
|
|
||||||
'Voice AI Razvoj',
|
|
||||||
'SaaS Razvoj',
|
|
||||||
'Fullstack Web Razvoj',
|
|
||||||
'n8n Automatizacija',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const schema = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'ProfessionalService',
|
|
||||||
'@id': `${baseUrl}/#business`,
|
|
||||||
name: 'Damjan Savić - AI & Automation Specialist',
|
|
||||||
description: locale === 'de'
|
|
||||||
? 'Freelance AI & Automation Specialist aus Köln. Spezialisiert auf KI-Agenten, Voice AI, Prozessautomatisierung und SaaS-Entwicklung.'
|
|
||||||
: locale === 'sr'
|
|
||||||
? 'Freelance AI & Automation Specialist iz Kelna. Specijalizovan za AI agente, Voice AI, automatizaciju procesa i SaaS razvoj.'
|
|
||||||
: 'Remote AI & Automation Specialist based in Germany, serving clients in USA, UK and Europe. Specialized in AI agents, Voice AI, n8n process automation and custom SaaS development.',
|
|
||||||
url: baseUrl,
|
|
||||||
logo: `${baseUrl}/images/og-image.jpg`,
|
|
||||||
image: `${baseUrl}/images/og-image.jpg`,
|
|
||||||
email: 'info@damjan-savic.com',
|
|
||||||
telephone: '+491756950979',
|
|
||||||
priceRange: '€€€',
|
|
||||||
address: {
|
|
||||||
'@type': 'PostalAddress',
|
|
||||||
streetAddress: 'Rotdornallee',
|
|
||||||
addressLocality: locale === 'sr' ? 'Keln' : locale === 'en' ? 'Cologne' : 'Köln',
|
|
||||||
addressRegion: 'NRW',
|
|
||||||
postalCode: '50769',
|
|
||||||
addressCountry: 'DE',
|
|
||||||
},
|
|
||||||
geo: {
|
|
||||||
'@type': 'GeoCoordinates',
|
|
||||||
latitude: 50.9375,
|
|
||||||
longitude: 6.9603,
|
|
||||||
},
|
|
||||||
areaServed: [
|
|
||||||
// Germany
|
|
||||||
{ '@type': 'City', name: 'Cologne' },
|
|
||||||
{ '@type': 'City', name: 'Düsseldorf' },
|
|
||||||
{ '@type': 'City', name: 'Frankfurt' },
|
|
||||||
{ '@type': 'City', name: 'Munich' },
|
|
||||||
{ '@type': 'City', name: 'Berlin' },
|
|
||||||
{ '@type': 'City', name: 'Hamburg' },
|
|
||||||
{ '@type': 'Country', name: 'Germany' },
|
|
||||||
// USA
|
|
||||||
{ '@type': 'City', name: 'New York' },
|
|
||||||
{ '@type': 'City', name: 'Los Angeles' },
|
|
||||||
{ '@type': 'City', name: 'San Francisco' },
|
|
||||||
{ '@type': 'City', name: 'Miami' },
|
|
||||||
{ '@type': 'City', name: 'Chicago' },
|
|
||||||
{ '@type': 'Country', name: 'United States' },
|
|
||||||
// UK
|
|
||||||
{ '@type': 'City', name: 'London' },
|
|
||||||
{ '@type': 'Country', name: 'United Kingdom' },
|
|
||||||
// Europe
|
|
||||||
{ '@type': 'Country', name: 'Austria' },
|
|
||||||
{ '@type': 'Country', name: 'Switzerland' },
|
|
||||||
],
|
|
||||||
hasOfferCatalog: {
|
|
||||||
'@type': 'OfferCatalog',
|
|
||||||
name: locale === 'de' ? 'Dienstleistungen' : locale === 'sr' ? 'Usluge' : 'Services',
|
|
||||||
itemListElement: serviceNames[locale].map((service, index) => ({
|
|
||||||
'@type': 'Offer',
|
|
||||||
itemOffered: {
|
|
||||||
'@type': 'Service',
|
|
||||||
name: service,
|
|
||||||
},
|
|
||||||
position: index + 1,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
founder: {
|
|
||||||
'@id': `${baseUrl}/#person`,
|
|
||||||
},
|
|
||||||
sameAs: [
|
|
||||||
'https://www.linkedin.com/in/damjansavic/',
|
|
||||||
'https://github.com/damjansavic',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function BreadcrumbJsonLd({
|
|
||||||
items,
|
|
||||||
locale,
|
|
||||||
}: {
|
|
||||||
items: { name: string; url: string }[];
|
|
||||||
locale: Locale;
|
|
||||||
}) {
|
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
|
|
||||||
|
|
||||||
const schema = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'BreadcrumbList',
|
|
||||||
inLanguage: locale === 'de' ? 'de-DE' : locale === 'sr' ? 'sr-RS' : 'en-US',
|
|
||||||
itemListElement: items.map((item, index) => ({
|
|
||||||
'@type': 'ListItem',
|
|
||||||
position: index + 1,
|
|
||||||
name: item.name,
|
|
||||||
item: item.url.startsWith('http') ? item.url : `${baseUrl}${item.url}`,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function WebPageJsonLd({
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
url,
|
|
||||||
locale,
|
|
||||||
}: {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
url: string;
|
|
||||||
locale: Locale;
|
|
||||||
}) {
|
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
|
|
||||||
|
|
||||||
const schema = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'WebPage',
|
|
||||||
name: title,
|
|
||||||
description: description,
|
|
||||||
url: url.startsWith('http') ? url : `${baseUrl}${url}`,
|
|
||||||
inLanguage: locale === 'de' ? 'de-DE' : locale === 'sr' ? 'sr-RS' : 'en-US',
|
|
||||||
isPartOf: {
|
|
||||||
'@type': 'WebSite',
|
|
||||||
name: 'Damjan Savić',
|
|
||||||
url: baseUrl,
|
|
||||||
},
|
|
||||||
author: {
|
|
||||||
'@id': `${baseUrl}/#person`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FAQItem {
|
|
||||||
question: string;
|
|
||||||
answer: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FAQJsonLd({ items, locale }: { items: FAQItem[]; locale?: Locale }) {
|
|
||||||
const schema = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'FAQPage',
|
|
||||||
inLanguage: locale ? (locale === 'de' ? 'de-DE' : locale === 'sr' ? 'sr-RS' : 'en-US') : 'de-DE',
|
|
||||||
mainEntity: items.map((item) => ({
|
|
||||||
'@type': 'Question',
|
|
||||||
name: item.question,
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: item.answer,
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ServiceJsonLdProps {
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
url: string;
|
|
||||||
locale: Locale;
|
|
||||||
areaServed?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ServiceJsonLd({
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
url,
|
|
||||||
locale,
|
|
||||||
areaServed = ['Köln', 'Düsseldorf', 'Frankfurt', 'Deutschland'],
|
|
||||||
}: ServiceJsonLdProps) {
|
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
|
|
||||||
|
|
||||||
const schema = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'Service',
|
|
||||||
name: name,
|
|
||||||
description: description,
|
|
||||||
url: url.startsWith('http') ? url : `${baseUrl}${url}`,
|
|
||||||
inLanguage: locale === 'de' ? 'de-DE' : locale === 'sr' ? 'sr-RS' : 'en-US',
|
|
||||||
availableLanguage: ['de-DE', 'en-US', 'sr-RS'],
|
|
||||||
provider: {
|
|
||||||
'@id': `${baseUrl}/#business`,
|
|
||||||
},
|
|
||||||
areaServed: areaServed.map((area) => ({
|
|
||||||
'@type': 'City',
|
|
||||||
name: area,
|
|
||||||
})),
|
|
||||||
serviceType: name,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Organization Schema
|
|
||||||
export function OrganizationJsonLd({ locale }: JsonLdProps) {
|
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
|
|
||||||
|
|
||||||
const schema = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'Organization',
|
|
||||||
'@id': `${baseUrl}/#organization`,
|
|
||||||
name: 'Damjan Savić - AI & Automation Specialist',
|
|
||||||
alternateName: 'Damjan Savić',
|
|
||||||
url: baseUrl,
|
|
||||||
logo: {
|
|
||||||
'@type': 'ImageObject',
|
|
||||||
url: `${baseUrl}/images/og-image.jpg`,
|
|
||||||
width: 1200,
|
|
||||||
height: 630,
|
|
||||||
},
|
|
||||||
image: `${baseUrl}/images/og-image.jpg`,
|
|
||||||
description: locale === 'de'
|
|
||||||
? 'Freelance KI-Entwickler und Automatisierungsspezialist aus Köln. Spezialisiert auf KI-Agenten, Voice AI, Prozessautomatisierung mit n8n und SaaS-Entwicklung.'
|
|
||||||
: locale === 'sr'
|
|
||||||
? 'Freelance AI developer i specijalista za automatizaciju iz Kelna. Specijalizovan za AI agente, Voice AI, automatizaciju procesa sa n8n i SaaS razvoj.'
|
|
||||||
: 'Remote AI developer and automation specialist based in Germany. Working with clients in USA, UK & Europe. Specialized in AI agents, Voice AI, n8n automation and SaaS development.',
|
|
||||||
email: 'info@damjan-savic.com',
|
|
||||||
telephone: '+491756950979',
|
|
||||||
address: {
|
|
||||||
'@type': 'PostalAddress',
|
|
||||||
streetAddress: 'Rotdornallee',
|
|
||||||
addressLocality: locale === 'sr' ? 'Keln' : locale === 'en' ? 'Cologne' : 'Köln',
|
|
||||||
addressRegion: 'NRW',
|
|
||||||
postalCode: '50769',
|
|
||||||
addressCountry: 'DE',
|
|
||||||
},
|
|
||||||
geo: {
|
|
||||||
'@type': 'GeoCoordinates',
|
|
||||||
latitude: 50.9375,
|
|
||||||
longitude: 6.9603,
|
|
||||||
},
|
|
||||||
areaServed: [
|
|
||||||
// DACH Region
|
|
||||||
{ '@type': 'Country', name: 'Germany' },
|
|
||||||
{ '@type': 'Country', name: 'Austria' },
|
|
||||||
{ '@type': 'Country', name: 'Switzerland' },
|
|
||||||
// International (Remote)
|
|
||||||
{ '@type': 'Country', name: 'United States' },
|
|
||||||
{ '@type': 'Country', name: 'United Kingdom' },
|
|
||||||
{ '@type': 'Country', name: 'Serbia' },
|
|
||||||
// Major International Cities
|
|
||||||
{ '@type': 'City', name: 'New York' },
|
|
||||||
{ '@type': 'City', name: 'London' },
|
|
||||||
{ '@type': 'City', name: 'San Francisco' },
|
|
||||||
{ '@type': 'City', name: 'Los Angeles' },
|
|
||||||
],
|
|
||||||
founder: {
|
|
||||||
'@id': `${baseUrl}/#person`,
|
|
||||||
},
|
|
||||||
sameAs: [
|
|
||||||
'https://www.linkedin.com/in/damjansavic/',
|
|
||||||
'https://github.com/damjansavic',
|
|
||||||
],
|
|
||||||
contactPoint: {
|
|
||||||
'@type': 'ContactPoint',
|
|
||||||
contactType: 'customer service',
|
|
||||||
email: 'info@damjan-savic.com',
|
|
||||||
telephone: '+491756950979',
|
|
||||||
availableLanguage: ['German', 'English', 'Serbian'],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Article Schema for Blog Posts
|
|
||||||
interface ArticleJsonLdProps {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
url: string;
|
|
||||||
imageUrl: string;
|
|
||||||
datePublished: string;
|
|
||||||
dateModified?: string;
|
|
||||||
authorName?: string;
|
|
||||||
tags?: string[];
|
|
||||||
locale: Locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ArticleJsonLd({
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
url,
|
|
||||||
imageUrl,
|
|
||||||
datePublished,
|
|
||||||
dateModified,
|
|
||||||
authorName = 'Damjan Savić',
|
|
||||||
tags = [],
|
|
||||||
locale,
|
|
||||||
}: ArticleJsonLdProps) {
|
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
|
|
||||||
|
|
||||||
const schema = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'Article',
|
|
||||||
headline: title,
|
|
||||||
description: description,
|
|
||||||
image: imageUrl.startsWith('http') ? imageUrl : `${baseUrl}${imageUrl}`,
|
|
||||||
url: url.startsWith('http') ? url : `${baseUrl}${url}`,
|
|
||||||
datePublished: datePublished,
|
|
||||||
dateModified: dateModified || datePublished,
|
|
||||||
author: {
|
|
||||||
'@type': 'Person',
|
|
||||||
'@id': `${baseUrl}/#person`,
|
|
||||||
name: authorName,
|
|
||||||
url: baseUrl,
|
|
||||||
},
|
|
||||||
publisher: {
|
|
||||||
'@type': 'Organization',
|
|
||||||
'@id': `${baseUrl}/#organization`,
|
|
||||||
name: 'Damjan Savić',
|
|
||||||
logo: {
|
|
||||||
'@type': 'ImageObject',
|
|
||||||
url: `${baseUrl}/images/og-image.jpg`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mainEntityOfPage: {
|
|
||||||
'@type': 'WebPage',
|
|
||||||
'@id': url.startsWith('http') ? url : `${baseUrl}${url}`,
|
|
||||||
},
|
|
||||||
inLanguage: locale === 'de' ? 'de-DE' : locale === 'sr' ? 'sr-RS' : 'en-US',
|
|
||||||
keywords: tags.join(', '),
|
|
||||||
articleSection: 'Technology',
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// HowTo Schema for Tutorials
|
|
||||||
interface HowToStep {
|
|
||||||
name: string;
|
|
||||||
text: string;
|
|
||||||
url?: string;
|
|
||||||
image?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface HowToJsonLdProps {
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
steps: HowToStep[];
|
|
||||||
totalTime?: string; // ISO 8601 duration format, e.g., "PT30M" for 30 minutes
|
|
||||||
estimatedCost?: { currency: string; value: string };
|
|
||||||
tools?: string[];
|
|
||||||
supplies?: string[];
|
|
||||||
image?: string;
|
|
||||||
locale: Locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function HowToJsonLd({
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
steps,
|
|
||||||
totalTime,
|
|
||||||
estimatedCost,
|
|
||||||
tools,
|
|
||||||
supplies,
|
|
||||||
image,
|
|
||||||
locale,
|
|
||||||
}: HowToJsonLdProps) {
|
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
|
|
||||||
|
|
||||||
const schema: Record<string, unknown> = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'HowTo',
|
|
||||||
name: name,
|
|
||||||
description: description,
|
|
||||||
step: steps.map((step, index) => ({
|
|
||||||
'@type': 'HowToStep',
|
|
||||||
position: index + 1,
|
|
||||||
name: step.name,
|
|
||||||
text: step.text,
|
|
||||||
...(step.url && { url: step.url.startsWith('http') ? step.url : `${baseUrl}${step.url}` }),
|
|
||||||
...(step.image && { image: step.image.startsWith('http') ? step.image : `${baseUrl}${step.image}` }),
|
|
||||||
})),
|
|
||||||
inLanguage: locale === 'de' ? 'de-DE' : locale === 'sr' ? 'sr-RS' : 'en-US',
|
|
||||||
};
|
|
||||||
|
|
||||||
if (totalTime) {
|
|
||||||
schema.totalTime = totalTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (estimatedCost) {
|
|
||||||
schema.estimatedCost = {
|
|
||||||
'@type': 'MonetaryAmount',
|
|
||||||
currency: estimatedCost.currency,
|
|
||||||
value: estimatedCost.value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tools && tools.length > 0) {
|
|
||||||
schema.tool = tools.map((tool) => ({
|
|
||||||
'@type': 'HowToTool',
|
|
||||||
name: tool,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (supplies && supplies.length > 0) {
|
|
||||||
schema.supply = supplies.map((supply) => ({
|
|
||||||
'@type': 'HowToSupply',
|
|
||||||
name: supply,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (image) {
|
|
||||||
schema.image = image.startsWith('http') ? image : `${baseUrl}${image}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProfilePage Schema for About Page
|
|
||||||
interface ProfilePageJsonLdProps {
|
|
||||||
locale: Locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ProfilePageJsonLd({ locale }: ProfilePageJsonLdProps) {
|
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
|
|
||||||
|
|
||||||
const schema = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'ProfilePage',
|
|
||||||
dateCreated: '2024-01-01',
|
|
||||||
dateModified: new Date().toISOString().split('T')[0],
|
|
||||||
mainEntity: {
|
|
||||||
'@id': `${baseUrl}/#person`,
|
|
||||||
},
|
|
||||||
name: locale === 'de'
|
|
||||||
? 'Über Damjan Savić - KI-Entwickler & Automatisierungsspezialist'
|
|
||||||
: locale === 'sr'
|
|
||||||
? 'O Damjanu Saviću - AI Developer & Specijalista za automatizaciju'
|
|
||||||
: 'About Damjan Savić - AI Developer & Automation Specialist',
|
|
||||||
description: locale === 'de'
|
|
||||||
? 'Erfahren Sie mehr über Damjan Savić, Freelance KI-Entwickler und Automatisierungsspezialist aus Köln. Expertise in KI-Agenten, Voice AI, n8n und SaaS-Entwicklung.'
|
|
||||||
: locale === 'sr'
|
|
||||||
? 'Saznajte više o Damjanu Saviću, freelance AI developeru i specijalisti za automatizaciju iz Kelna. Ekspertiza u AI agentima, Voice AI, n8n i SaaS razvoju.'
|
|
||||||
: 'Learn more about Damjan Savić, freelance AI developer and automation specialist from Cologne. Expertise in AI agents, Voice AI, n8n and SaaS development.',
|
|
||||||
url: `${baseUrl}/${locale}/about`,
|
|
||||||
inLanguage: locale === 'de' ? 'de-DE' : locale === 'sr' ? 'sr-RS' : 'en-US',
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// VideoObject Schema for embedded videos
|
|
||||||
interface VideoJsonLdProps {
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
thumbnailUrl: string;
|
|
||||||
uploadDate: string;
|
|
||||||
duration?: string; // ISO 8601 format, e.g., "PT1M30S"
|
|
||||||
contentUrl?: string;
|
|
||||||
embedUrl?: string;
|
|
||||||
locale: Locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function VideoJsonLd({
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
thumbnailUrl,
|
|
||||||
uploadDate,
|
|
||||||
duration,
|
|
||||||
contentUrl,
|
|
||||||
embedUrl,
|
|
||||||
locale,
|
|
||||||
}: VideoJsonLdProps) {
|
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
|
|
||||||
|
|
||||||
const schema: Record<string, unknown> = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'VideoObject',
|
|
||||||
name: name,
|
|
||||||
description: description,
|
|
||||||
thumbnailUrl: thumbnailUrl.startsWith('http') ? thumbnailUrl : `${baseUrl}${thumbnailUrl}`,
|
|
||||||
uploadDate: uploadDate,
|
|
||||||
inLanguage: locale === 'de' ? 'de-DE' : locale === 'sr' ? 'sr-RS' : 'en-US',
|
|
||||||
author: {
|
|
||||||
'@id': `${baseUrl}/#person`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (duration) {
|
|
||||||
schema.duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contentUrl) {
|
|
||||||
schema.contentUrl = contentUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (embedUrl) {
|
|
||||||
schema.embedUrl = embedUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SoftwareApplication Schema for tools/projects
|
|
||||||
interface SoftwareAppJsonLdProps {
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
applicationCategory: string;
|
|
||||||
operatingSystem?: string;
|
|
||||||
offers?: {
|
|
||||||
price: string;
|
|
||||||
priceCurrency: string;
|
|
||||||
};
|
|
||||||
aggregateRating?: {
|
|
||||||
ratingValue: string;
|
|
||||||
ratingCount: string;
|
|
||||||
};
|
|
||||||
locale: Locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SoftwareAppJsonLd({
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
applicationCategory,
|
|
||||||
operatingSystem = 'Web',
|
|
||||||
offers,
|
|
||||||
aggregateRating,
|
|
||||||
locale,
|
|
||||||
}: SoftwareAppJsonLdProps) {
|
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
|
|
||||||
|
|
||||||
const schema: Record<string, unknown> = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'SoftwareApplication',
|
|
||||||
name: name,
|
|
||||||
description: description,
|
|
||||||
applicationCategory: applicationCategory,
|
|
||||||
operatingSystem: operatingSystem,
|
|
||||||
author: {
|
|
||||||
'@id': `${baseUrl}/#person`,
|
|
||||||
},
|
|
||||||
inLanguage: locale === 'de' ? 'de-DE' : locale === 'sr' ? 'sr-RS' : 'en-US',
|
|
||||||
};
|
|
||||||
|
|
||||||
if (offers) {
|
|
||||||
schema.offers = {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: offers.price,
|
|
||||||
priceCurrency: offers.priceCurrency,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aggregateRating) {
|
|
||||||
schema.aggregateRating = {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: aggregateRating.ratingValue,
|
|
||||||
ratingCount: aggregateRating.ratingCount,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebSite Schema for Search Box
|
|
||||||
export function WebSiteJsonLd({ locale }: JsonLdProps) {
|
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
|
|
||||||
|
|
||||||
const schema = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'WebSite',
|
|
||||||
'@id': `${baseUrl}/#website`,
|
|
||||||
name: 'Damjan Savić',
|
|
||||||
alternateName: locale === 'de'
|
|
||||||
? 'Damjan Savić - KI Entwickler Köln'
|
|
||||||
: locale === 'sr'
|
|
||||||
? 'Damjan Savić - AI Developer Keln'
|
|
||||||
: 'Damjan Savić - AI Developer Cologne',
|
|
||||||
url: baseUrl,
|
|
||||||
description: locale === 'de'
|
|
||||||
? 'Portfolio und Blog von Damjan Savić - KI-Entwickler und Automatisierungsspezialist aus Köln'
|
|
||||||
: locale === 'sr'
|
|
||||||
? 'Portfolio i blog Damjana Savića - AI developer i specijalista za automatizaciju iz Kelna'
|
|
||||||
: 'Portfolio and blog of Damjan Savić - AI Developer and Automation Specialist from Cologne',
|
|
||||||
publisher: {
|
|
||||||
'@id': `${baseUrl}/#organization`,
|
|
||||||
},
|
|
||||||
inLanguage: [
|
|
||||||
{ '@type': 'Language', name: 'German', alternateName: 'de' },
|
|
||||||
{ '@type': 'Language', name: 'English', alternateName: 'en' },
|
|
||||||
{ '@type': 'Language', name: 'Serbian', alternateName: 'sr' },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<script
|
|
||||||
type="application/ld+json"
|
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user