d49c9aa225
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
203 lines
5.0 KiB
TypeScript
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,
|
|
},
|
|
}}
|
|
/>
|
|
);
|
|
}
|