feat(seo): GEO paketi — yapılandırılmış metadata, AI bot erişimi, veri tazelik sinyali
- sayfaMetadata(): indekslenebilir sayfalar için tek kaynaklı canonical + eksiksiz openGraph üretimi; sayfalar buna geçirildi - robots.ts: yalnızca /api/ engellenir; noindex'li sayfalar robots engeli yerine meta ile yönetilir, AI botları niyeti belgeleyen açık grupla izinli - next.config: htmlLimitedBots'a AI botları eklendi (JS çalıştırmayan botlara metadata head içinde bloklanarak verilir) - sitemap lastmod + katalogda görünür 'Kaynak/son güncelleme' satırı (VeriKaynagi) KATALOG_VERI_TARIHI tek kaynağından - docs/geo/geo-raporu.md: çalışmanın raporu Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,9 +8,15 @@ import {
|
||||
getBolumBySlug,
|
||||
uniSlugFromAd,
|
||||
} from "../katalog-queries";
|
||||
import { JsonLd, breadcrumbJsonLd } from "@/lib/seo";
|
||||
import {
|
||||
JsonLd,
|
||||
breadcrumbJsonLd,
|
||||
sayfaMetadata,
|
||||
ustSegmentOgGorseli,
|
||||
} from "@/lib/seo";
|
||||
import { SAYFA_BOYU } from "./sayfalama";
|
||||
import { BolumProgramListesi } from "./bolum-program-listesi";
|
||||
import { VeriKaynagi } from "./veri-kaynagi";
|
||||
import { SiteFooter } from "@/components/site-footer";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
|
||||
@@ -41,21 +47,27 @@ export function bolumSayfaMetadata(slug: string, sayfa: number): Metadata {
|
||||
if (!bolum) return {};
|
||||
const { stats } = bolum;
|
||||
if (sayfa > 1) {
|
||||
return {
|
||||
return sayfaMetadata({
|
||||
title: `${bolum.ad} Taban Puanları 2026 (Sayfa ${sayfa})`,
|
||||
description: `${bolum.ad} taban puanları ve başarı sıralamaları tablosunun ${sayfa}. sayfası — toplam ${stats.toplam} programın 2025 yerleştirme verileri.`,
|
||||
alternates: { canonical: `/bolum/${slug}/sayfa/${sayfa}` },
|
||||
};
|
||||
path: `/bolum/${slug}/sayfa/${sayfa}`,
|
||||
openGraph: {
|
||||
images: ustSegmentOgGorseli(
|
||||
`/bolum/${slug}`,
|
||||
"Bölüm taban puanları ve sıralamaları — KolayTercih",
|
||||
),
|
||||
},
|
||||
});
|
||||
}
|
||||
const enIyi =
|
||||
stats.minSira != null
|
||||
? ` En iyi taban sıralaması ${sayi(stats.minSira)}.`
|
||||
: "";
|
||||
return {
|
||||
return sayfaMetadata({
|
||||
title: `${bolum.ad} Taban Puanları ve Başarı Sıralamaları 2026`,
|
||||
description: `${bolum.ad} 2025 yerleştirme verileri: ${stats.toplam} programın taban puanı ve sıralaması, ${stats.devlet} devlet / ${stats.vakif} vakıf.${enIyi} 2026 tercih rehberi.`,
|
||||
alternates: { canonical: `/bolum/${slug}` },
|
||||
};
|
||||
path: `/bolum/${slug}`,
|
||||
});
|
||||
}
|
||||
|
||||
export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
|
||||
@@ -139,6 +151,7 @@ export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
|
||||
tabanYol={`/bolum/${slug}`}
|
||||
sayfa={sayfa}
|
||||
/>
|
||||
<VeriKaynagi className="mt-6" />
|
||||
</section>
|
||||
</main>
|
||||
<SiteFooter />
|
||||
|
||||
@@ -9,7 +9,13 @@ import {
|
||||
bolumSlugFromIsim,
|
||||
} from "../katalog-queries";
|
||||
import { trBaslikDuzeni } from "@/lib/slug";
|
||||
import { JsonLd, breadcrumbJsonLd, SITE_URL } from "@/lib/seo";
|
||||
import {
|
||||
JsonLd,
|
||||
breadcrumbJsonLd,
|
||||
sayfaMetadata,
|
||||
SITE_URL,
|
||||
ustSegmentOgGorseli,
|
||||
} from "@/lib/seo";
|
||||
import { CtaSiraForm } from "@/features/sihirbaz/components/cta-sira-form";
|
||||
import { SiteFooter } from "@/components/site-footer";
|
||||
import { Sayfalama, SAYFA_BOYU } from "./sayfalama";
|
||||
@@ -17,6 +23,7 @@ import { PagePixelDivider } from "@/components/pixel-decor";
|
||||
import { UniversiteKonumHaritasi } from "./universite-konum-haritasi";
|
||||
import { uniUlkeKonum } from "@/lib/harita-ulkeler";
|
||||
import { UniversiteProgramTablosu } from "./universite-program-tablosu";
|
||||
import { VeriKaynagi } from "./veri-kaynagi";
|
||||
import { UniLogo } from "@/components/uni-logo";
|
||||
import { uniLogoYolu } from "@/lib/uni-logolar";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
@@ -48,17 +55,23 @@ export function uniSayfaMetadata(slug: string, sayfa: number): Metadata {
|
||||
const uni = getUniversiteBySlug(slug);
|
||||
if (!uni) return {};
|
||||
if (sayfa > 1) {
|
||||
return {
|
||||
return sayfaMetadata({
|
||||
title: `${uni.ad} Taban Puanları 2026 (Sayfa ${sayfa})`,
|
||||
description: `${uni.ad} taban puanları ve başarı sıralamaları tablosunun ${sayfa}. sayfası — toplam ${uni.programSayisi} programın 2025 yerleştirme verileri.`,
|
||||
alternates: { canonical: `/universite/${slug}/sayfa/${sayfa}` },
|
||||
};
|
||||
path: `/universite/${slug}/sayfa/${sayfa}`,
|
||||
openGraph: {
|
||||
images: ustSegmentOgGorseli(
|
||||
`/universite/${slug}`,
|
||||
"Üniversite taban puanları ve sıralamaları — KolayTercih",
|
||||
),
|
||||
},
|
||||
});
|
||||
}
|
||||
return {
|
||||
return sayfaMetadata({
|
||||
title: `${uni.ad} Taban Puanları ve Başarı Sıralamaları 2026`,
|
||||
description: `${uni.ad} 2025 yerleştirme verileri: ${uni.programSayisi} programın taban puanları, başarı sıralamaları ve kontenjanları${uni.il ? ` (${trBaslikDuzeni(uni.il)})` : ""}. 2026 tercih rehberi.`,
|
||||
alternates: { canonical: `/universite/${slug}` },
|
||||
};
|
||||
path: `/universite/${slug}`,
|
||||
});
|
||||
}
|
||||
|
||||
export function UniversiteIcerik({
|
||||
@@ -274,11 +287,10 @@ export function UniversiteIcerik({
|
||||
|
||||
<UniversiteProgramTablosu gruplar={gruplar} />
|
||||
|
||||
<p className="mt-3 text-xs leading-5 text-slate-500">
|
||||
<VeriKaynagi>
|
||||
"—": o yıl için yerleştirme verisi bulunmuyor (yeni açılan
|
||||
veya o yıl yerleşen çıkmayan program). Kaynak: YÖK Atlas 2025
|
||||
yerleştirme sonuçları.
|
||||
</p>
|
||||
veya o yıl yerleşen çıkmayan program).{" "}
|
||||
</VeriKaynagi>
|
||||
|
||||
<Sayfalama
|
||||
tabanYol={`/universite/${slug}`}
|
||||
|
||||
29
src/features/katalog/components/veri-kaynagi.tsx
Normal file
29
src/features/katalog/components/veri-kaynagi.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { KATALOG_VERI_TARIHI } from "@/lib/seo";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const TARIH_METNI = new Date(
|
||||
`${KATALOG_VERI_TARIHI}T00:00:00Z`,
|
||||
).toLocaleDateString("tr-TR", {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
timeZone: "UTC",
|
||||
});
|
||||
|
||||
// Katalog tablolarının altındaki kaynak + tazelik satırı. Görünür metin olması
|
||||
// bilinçli: arama/AI botları JSON-LD'ye değil sayfadaki metne güvenir.
|
||||
export function VeriKaynagi({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<p className={cn("mt-3 text-xs leading-5 text-slate-500", className)}>
|
||||
{children}
|
||||
Kaynak: YÖK Atlas 2025 yerleştirme sonuçları. Son güncelleme:{" "}
|
||||
<time dateTime={KATALOG_VERI_TARIHI}>{TARIH_METNI}</time>.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user