auto-claude: subtask-1-9 - Extract HowToJsonLd component

This commit is contained in:
2026-01-25 06:40:04 +01:00
parent 4e9fec53eb
commit 6043aca819
@@ -0,0 +1,87 @@
import { type Locale } from '@/i18n/config';
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) }}
/>
);
}