From e3cf141f55c4c0e0de35c04a6943d8402d497db4 Mon Sep 17 00:00:00 2001 From: bilalgursen Date: Sat, 8 Aug 2026 01:18:22 +0300 Subject: [PATCH] =?UTF-8?q?refactor:=20rehber=20feature'=C4=B1=20=E2=80=94?= =?UTF-8?q?=20makale=20ve=20liste=20feature=20bile=C5=9Fenlerine=20=C3=A7?= =?UTF-8?q?=C4=B1kt=C4=B1=20(Faz=203)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - rehber-kapak + el-cizimi-sema features/rehber/components/ altına taşındı (el-cizimi-sema'nın göreli font yolu yeni konuma göre düzeltildi) - /rehber'in kart gridi RehberListesi'ne, /rehber/[slug]'ın makale gövdesi (JsonLd + içerik + ilgili yazılar + notFound) RehberMakale'ye çıkarıldı; metadata/staticParams yardımcıları bileşen dosyasından export ediliyor - İki sayfa senkron: params.then() + sayfada Suspense, skeleton'lar colocated - rehber-queries.ts (server-only) lib/rehber'in feature kapısı - Davranış değişikliği yok; içerik build-time statik Co-Authored-By: Claude Fable 5 --- src/app/meraklisina/huni-semasi.tsx | 4 +- src/app/rehber/[slug]/opengraph-image.tsx | 2 +- src/app/rehber/[slug]/page.tsx | 163 ++++-------------- src/app/rehber/page.tsx | 82 +++------ .../katalog/components/katalog-arama.tsx | 2 +- .../rehber}/components/el-cizimi-sema.tsx | 2 +- .../rehber}/components/rehber-kapak.tsx | 0 .../rehber/components/rehber-listesi.tsx | 53 ++++++ .../rehber/components/rehber-makale.tsx | 138 +++++++++++++++ src/features/rehber/rehber-queries.ts | 11 ++ 10 files changed, 266 insertions(+), 191 deletions(-) rename src/{ => features/rehber}/components/el-cizimi-sema.tsx (98%) rename src/{ => features/rehber}/components/rehber-kapak.tsx (100%) create mode 100644 src/features/rehber/components/rehber-listesi.tsx create mode 100644 src/features/rehber/components/rehber-makale.tsx create mode 100644 src/features/rehber/rehber-queries.ts diff --git a/src/app/meraklisina/huni-semasi.tsx b/src/app/meraklisina/huni-semasi.tsx index 8571752..48ee426 100644 --- a/src/app/meraklisina/huni-semasi.tsx +++ b/src/app/meraklisina/huni-semasi.tsx @@ -1,8 +1,8 @@ // Sihirbaz hunisinin statik anlatımı: interaktif demo yerine akışın tamamını // tek bakışta gösteren bir mermaid şeması. Çizim, ürün genelindeki ortak -// "el çizimi" bileşeniyle yapılır (bkz. components/el-cizimi-sema.tsx). +// "el çizimi" bileşeniyle yapılır (bkz. features/rehber/components/el-cizimi-sema.tsx). -import { ElCizimiSema } from "@/components/el-cizimi-sema"; +import { ElCizimiSema } from "@/features/rehber/components/el-cizimi-sema"; // Renkler ürünün risk dilinden (kırmızı/sarı/yeşil) ve slate paletinden; // renk tek başına anlam taşımaz, her düğüm metinle etiketlidir. diff --git a/src/app/rehber/[slug]/opengraph-image.tsx b/src/app/rehber/[slug]/opengraph-image.tsx index 33bc1ec..62f4553 100644 --- a/src/app/rehber/[slug]/opengraph-image.tsx +++ b/src/app/rehber/[slug]/opengraph-image.tsx @@ -4,7 +4,7 @@ import { loadRehberOgFonts } from "@/lib/og-fonts"; import { rehberBasliginiBol, rehberMetniniParcala, -} from "@/components/rehber-kapak"; +} from "@/features/rehber/components/rehber-kapak"; export const alt = "KolayTercih Tercih Rehberi yazı kapağı"; export const size = { width: 1200, height: 630 }; diff --git a/src/app/rehber/[slug]/page.tsx b/src/app/rehber/[slug]/page.tsx index c547b95..eb6ec53 100644 --- a/src/app/rehber/[slug]/page.tsx +++ b/src/app/rehber/[slug]/page.tsx @@ -1,153 +1,52 @@ +import { Suspense } from "react"; import type { Metadata } from "next"; import Link from "next/link"; -import { notFound } from "next/navigation"; import { ChevronRight } from "lucide-react"; -import { getAllRehberSlugs, getAllRehberler, getRehber } from "@/lib/rehber"; -import { JsonLd, articleJsonLd, breadcrumbJsonLd } from "@/lib/seo"; -import { CtaSiraForm } from "@/components/cta-sira-form"; -import { ElCizimiSema } from "@/components/el-cizimi-sema"; -import { RehberKapak } from "@/components/rehber-kapak"; import { SiteFooter } from "@/components/site-footer"; +import { + RehberMakale, + RehberMakaleSkeleton, + rehberStaticSlugs, + rehberYaziMetadata, +} from "@/features/rehber/components/rehber-makale"; export function generateStaticParams() { - const slugs = getAllRehberSlugs(); - if (slugs.length === 0) { - throw new Error("rehber: content/rehber altında hiç makale yok"); - } - return slugs.map((slug) => ({ slug })); + return rehberStaticSlugs().map((slug) => ({ slug })); } export async function generateMetadata({ params, -}: { - params: Promise<{ slug: string }>; -}): Promise { +}: PageProps<"/rehber/[slug]">): Promise { const { slug } = await params; - const yazi = getRehber(slug); - if (!yazi) return {}; - return { - title: yazi.baslik, - description: yazi.aciklama, - alternates: { canonical: `/rehber/${slug}` }, - openGraph: { - type: "article", - title: yazi.baslik, - description: yazi.aciklama, - publishedTime: yazi.tarih, - }, - }; + return rehberYaziMetadata(slug); } -export default async function RehberYaziPage({ +export default function RehberYaziPage({ params, -}: { - params: Promise<{ slug: string }>; -}) { - const { slug } = await params; - const yazi = getRehber(slug); - if (!yazi) notFound(); - - const digerYazilar = getAllRehberler() - .filter((y) => y.slug !== slug) - .slice(0, 4); - +}: PageProps<"/rehber/[slug]">) { return ( <> -
- - - - - -
- -

- {yazi.baslik} -

-

- {yazi.aciklama} -

-
+
+ + Ana Sayfa + + + + Rehber + + -
- -
- - {digerYazilar.length > 0 ? ( -
-

- Diğer rehber yazıları -

-
    - {digerYazilar.map((y) => ( -
  • - - {y.baslik} - -
  • - ))} -
-
- ) : null} -
- + }> + {params.then(({ slug }) => ( + + ))} + + + ); } diff --git a/src/app/rehber/page.tsx b/src/app/rehber/page.tsx index 6f20f1b..4d4590f 100644 --- a/src/app/rehber/page.tsx +++ b/src/app/rehber/page.tsx @@ -1,12 +1,13 @@ +import { Suspense } from "react"; import type { Metadata } from "next"; -import Link from "next/link"; -import { ArrowRight } from "lucide-react"; -import { getAllRehberler } from "@/lib/rehber"; import { JsonLd, breadcrumbJsonLd } from "@/lib/seo"; import { CtaSiraForm } from "@/components/cta-sira-form"; -import { RehberKapak } from "@/components/rehber-kapak"; import { SiteFooter } from "@/components/site-footer"; import { PagePixelDivider } from "@/components/pixel-decor"; +import { + RehberListesi, + RehberListesiSkeleton, +} from "@/features/rehber/components/rehber-listesi"; export const metadata: Metadata = { title: "Tercih Rehberi 2026 — YKS Tercih Dönemi Yazıları", @@ -16,62 +17,35 @@ export const metadata: Metadata = { }; export default function RehberIndexPage() { - const yazilar = getAllRehberler(); - return ( <> -
- +
+ -

- Tercih Rehberi -

-

- Tercih döneminin en çok sorulan soruları, veriye dayalı ve öğrenci - diliyle. Panik yok, pazarlama yok — sadece işine yarayacak bilgi. -

+

+ Tercih Rehberi +

+

+ Tercih döneminin en çok sorulan soruları, veriye dayalı ve öğrenci + diliyle. Panik yok, pazarlama yok — sadece işine yarayacak bilgi. +

- + -
    - {yazilar.map((y) => ( -
  • - - -
    -

    - {y.aciklama} -

    - - Yazıyı oku - - -
    - -
  • - ))} -
+ }> + + -
- -
-
- +
+ +
+
+ ); } diff --git a/src/features/katalog/components/katalog-arama.tsx b/src/features/katalog/components/katalog-arama.tsx index 909f21a..79ab40f 100644 --- a/src/features/katalog/components/katalog-arama.tsx +++ b/src/features/katalog/components/katalog-arama.tsx @@ -4,7 +4,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { AramaFunnelKarti } from "@/components/arama-funnel-karti"; -import { RehberKapak } from "@/components/rehber-kapak"; +import { RehberKapak } from "@/features/rehber/components/rehber-kapak"; import { UniLogo } from "@/components/uni-logo"; import { aramaNormalize, eslesmePuani } from "@/lib/arama"; import type { RehberOzet } from "@/lib/rehber"; diff --git a/src/components/el-cizimi-sema.tsx b/src/features/rehber/components/el-cizimi-sema.tsx similarity index 98% rename from src/components/el-cizimi-sema.tsx rename to src/features/rehber/components/el-cizimi-sema.tsx index 4d527aa..29c461f 100644 --- a/src/components/el-cizimi-sema.tsx +++ b/src/features/rehber/components/el-cizimi-sema.tsx @@ -14,7 +14,7 @@ import localFont from "next/font/local"; // var). DİKKAT: lisans "kişisel kullanım için ücretsiz" — canlıya çıkmadan // mistifonts.com/sweet-cucumber-mocktail adresinden ticari lisans alınmalı. const elYazisi = localFont({ - src: "../fonts/SweetCucumberMocktail.woff2", + src: "../../../fonts/SweetCucumberMocktail.woff2", display: "swap", }); diff --git a/src/components/rehber-kapak.tsx b/src/features/rehber/components/rehber-kapak.tsx similarity index 100% rename from src/components/rehber-kapak.tsx rename to src/features/rehber/components/rehber-kapak.tsx diff --git a/src/features/rehber/components/rehber-listesi.tsx b/src/features/rehber/components/rehber-listesi.tsx new file mode 100644 index 0000000..6f807f1 --- /dev/null +++ b/src/features/rehber/components/rehber-listesi.tsx @@ -0,0 +1,53 @@ +import Link from "next/link"; +import { ArrowRight } from "lucide-react"; +import { getAllRehberler } from "../rehber-queries"; +import { RehberKapak } from "./rehber-kapak"; +import { Skeleton } from "@/components/ui/skeleton"; + +export function RehberListesi() { + const yazilar = getAllRehberler(); + + return ( +
    + {yazilar.map((y) => ( +
  • + + +
    +

    {y.aciklama}

    + + Yazıyı oku + + +
    + +
  • + ))} +
+ ); +} + +export function RehberListesiSkeleton() { + return ( +
    + {Array.from({ length: 3 }, (_, i) => ( +
  • +
    + +
    + + +
    +
    +
  • + ))} +
+ ); +} diff --git a/src/features/rehber/components/rehber-makale.tsx b/src/features/rehber/components/rehber-makale.tsx new file mode 100644 index 0000000..8f162a0 --- /dev/null +++ b/src/features/rehber/components/rehber-makale.tsx @@ -0,0 +1,138 @@ +import type { Metadata } from "next"; +import Link from "next/link"; +import { notFound } from "next/navigation"; +import { getAllRehberler, getAllRehberSlugs, getRehber } from "../rehber-queries"; +import { JsonLd, articleJsonLd, breadcrumbJsonLd } from "@/lib/seo"; +import { CtaSiraForm } from "@/components/cta-sira-form"; +import { ElCizimiSema } from "./el-cizimi-sema"; +import { RehberKapak } from "./rehber-kapak"; +import { Skeleton } from "@/components/ui/skeleton"; + +// Sayfanın generateStaticParams'ı için — sayfalar *-queries import etmez. +export function rehberStaticSlugs(): string[] { + const slugs = getAllRehberSlugs(); + if (slugs.length === 0) { + throw new Error("rehber: content/rehber altında hiç makale yok"); + } + return slugs; +} + +export function rehberYaziMetadata(slug: string): Metadata { + const yazi = getRehber(slug); + if (!yazi) return {}; + return { + title: yazi.baslik, + description: yazi.aciklama, + alternates: { canonical: `/rehber/${slug}` }, + openGraph: { + type: "article", + title: yazi.baslik, + description: yazi.aciklama, + publishedTime: yazi.tarih, + }, + }; +} + +export function RehberMakale({ slug }: { slug: string }) { + const yazi = getRehber(slug); + if (!yazi) notFound(); + + const digerYazilar = getAllRehberler() + .filter((y) => y.slug !== slug) + .slice(0, 4); + + return ( + <> + + + +
+ +

+ {yazi.baslik} +

+

+ {yazi.aciklama} +

+
+ {yazi.parcalar.map((parca, i) => + parca.tip === "html" ? ( +
+ ) : ( + + ), + )} +
+
+ +
+ +
+ + {digerYazilar.length > 0 ? ( +
+

+ Diğer rehber yazıları +

+
    + {digerYazilar.map((y) => ( +
  • + + {y.baslik} + +
  • + ))} +
+
+ ) : null} + + ); +} + +export function RehberMakaleSkeleton() { + return ( +
+ + +
+ {Array.from({ length: 5 }, (_, i) => ( + + ))} +
+
+ ); +} diff --git a/src/features/rehber/rehber-queries.ts b/src/features/rehber/rehber-queries.ts new file mode 100644 index 0000000..cf584fb --- /dev/null +++ b/src/features/rehber/rehber-queries.ts @@ -0,0 +1,11 @@ +import "server-only"; + +// Rehber feature'ının sorgu kapısı. İçerik motoru @/lib/rehber'de yaşar +// (content/rehber'i build sırasında fs'ten okur); feature bileşenleri ve +// sayfa yardımcıları yalnızca buradan okur. +export { + getAllRehberler, + getAllRehberSlugs, + getRehber, + type RehberOzet, +} from "@/lib/rehber";