Files
kovakyazilim/components/json-ld.tsx
T
egecankomur d49c9aa225 feat: SEO altyapısı + admin editör/favicon/menü düzeltmeleri
Admin & site:
- @tailwindcss/typography ekle → editör ve yayın içeriği prose stilleriyle düzgün render
- Favicon: logo.png'den kare app/icon.png + apple-icon.png, varsayılan favicon.ico kaldırıldı
- SEO keyword: seo_settings.default_keywords + seo_pages.keywords + buildMetadata birleştirme
- Menü düzeni admin'den yönetilebilir (site_settings.nav_items, /admin/menu, header & mobile-menu refactor)

SEO:
- app/sitemap.ts (statik + blog/hizmet/çözüm/proje/sektör dinamik)
- app/robots.ts (sitemap ref + /admin,/api disallow)
- app/llms.txt/route.ts (AI/LLM rehberi)
- BlogPosting/Service/FAQ/Article JSON-LD wire (json-ld bileşenleri bağlandı)
- buildMetadata: blog/proje OG görseli + type article + keywords birleştirme düzeltmesi
- blog tags → keyword
2026-06-04 07:15:18 +03:00

203 lines
5.0 KiB
TypeScript

import type {
BlogPostRow,
ProjectRow,
ServiceRow,
SiteSettingsRow,
FaqItem,
} from "@/lib/types";
import { siteConfig } from "@/lib/site-config";
function absUrl(url?: string | null): string | undefined {
if (!url) return undefined;
if (url.startsWith("http")) return url;
return `${siteConfig.url}${url.startsWith("/") ? "" : "/"}${url}`;
}
export function JsonLd({ data }: { data: object }) {
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
/>
);
}
export function OrganizationLd({
settings,
}: {
settings?: SiteSettingsRow | null;
}) {
const phone = settings?.contact_phone_raw ?? siteConfig.contact.phoneRaw;
const email = settings?.contact_email ?? siteConfig.contact.email;
const address = settings?.contact_address ?? siteConfig.contact.address;
const socials = [
settings?.social_linkedin,
settings?.social_instagram,
settings?.social_twitter,
settings?.social_facebook,
].filter(Boolean);
const data: Record<string, unknown> = {
"@context": "https://schema.org",
"@type": "LocalBusiness",
"@id": `${siteConfig.url}/#organization`,
name: settings?.site_name ?? siteConfig.name,
description: settings?.footer_tagline ?? siteConfig.tagline,
url: siteConfig.url,
logo: `${siteConfig.url}/logo.png`,
image: `${siteConfig.url}/logo.png`,
telephone: phone,
email,
address: {
"@type": "PostalAddress",
streetAddress: address,
addressLocality: "İzmit",
addressRegion: "Kocaeli",
addressCountry: "TR",
},
areaServed: [
{ "@type": "City", name: "Kocaeli" },
{ "@type": "City", name: "İstanbul" },
{ "@type": "Country", name: "Türkiye" },
],
sameAs: socials,
};
if (settings?.google_rating && settings?.google_review_count) {
data.aggregateRating = {
"@type": "AggregateRating",
ratingValue: settings.google_rating,
reviewCount: settings.google_review_count,
bestRating: 5,
worstRating: 1,
};
}
return <JsonLd data={data} />;
}
export function ServiceLd({
service,
settings,
}: {
service: ServiceRow;
settings?: SiteSettingsRow | null;
}) {
return (
<JsonLd
data={{
"@context": "https://schema.org",
"@type": "Service",
serviceType: service.title,
name: service.title,
description: service.description,
provider: {
"@type": "LocalBusiness",
name: settings?.site_name ?? siteConfig.name,
url: siteConfig.url,
},
areaServed: { "@type": "Country", name: "Türkiye" },
url: `${siteConfig.url}/hizmetler/${service.slug}`,
}}
/>
);
}
export function FaqLd({ items }: { items: FaqItem[] }) {
if (items.length === 0) return null;
return (
<JsonLd
data={{
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: items.map((it) => ({
"@type": "Question",
name: it.q,
acceptedAnswer: {
"@type": "Answer",
text: it.a,
},
})),
}}
/>
);
}
export function BreadcrumbLd({
items,
}: {
items: { name: string; url: string }[];
}) {
return (
<JsonLd
data={{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: items.map((it, i) => ({
"@type": "ListItem",
position: i + 1,
name: it.name,
item: it.url,
})),
}}
/>
);
}
export function BlogPostingLd({
post,
settings,
}: {
post: BlogPostRow;
settings?: SiteSettingsRow | null;
}) {
const image = absUrl(post.seo_image || post.cover_image) ?? `${siteConfig.url}/logo.png`;
const url = `${siteConfig.url}/blog/${post.slug}`;
const data: Record<string, unknown> = {
"@context": "https://schema.org",
"@type": "BlogPosting",
headline: post.title,
description: post.seo_description || post.excerpt || undefined,
image,
datePublished: post.published_at ?? post.$createdAt,
dateModified: post.$updatedAt,
author: {
"@type": post.author ? "Person" : "Organization",
name: post.author || settings?.site_name || siteConfig.name,
},
publisher: {
"@type": "Organization",
name: settings?.site_name ?? siteConfig.name,
logo: {
"@type": "ImageObject",
url: `${siteConfig.url}/logo.png`,
},
},
mainEntityOfPage: { "@type": "WebPage", "@id": url },
url,
};
if (post.tags && post.tags.length > 0) {
data.keywords = post.tags.join(", ");
}
return <JsonLd data={data} />;
}
export function ArticleLd({ post }: { post: ProjectRow }) {
return (
<JsonLd
data={{
"@context": "https://schema.org",
"@type": "Article",
headline: post.title,
description: post.description,
image: post.image_url,
author: {
"@type": "Organization",
name: siteConfig.name,
url: siteConfig.url,
},
}}
/>
);
}