From 95bc87e5f71af7e4d754834e0e108a82c241996b Mon Sep 17 00:00:00 2001 From: bilalgursen Date: Mon, 21 Sep 2026 23:34:52 +0300 Subject: [PATCH] =?UTF-8?q?fix(paket):=20giri=C5=9Fsiz=20ziyaret=C3=A7ide?= =?UTF-8?q?=20bo=C5=9F=20sayfa=20=E2=80=94=20kart=20g=C3=B6vdesi=20statik?= =?UTF-8?q?=20kabukta,=20yaln=C4=B1z=20CTA=20dinamik?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kök neden: React Flight ~3,2 KB'ı aşan dinamik parçayı ayrı satırlara bölüyor; PPR resume bunlara verdiği segment kimliğini kabukta geç tamamlanan sınırın (kök loading.tsx) kimliğiyle çakıştırıyor (S:3 iki kez) → HierarchyRequestError. Lokal prod build'de yeniden üretildi, düzeltmeden sonra 5/5 temiz. Girişsiz CTA metni: "ilk 3 tercihini ücretsiz gör" (sayılar sabitlerden). Co-Authored-By: Claude Fable 5.1 --- src/app/paket/page.tsx | 11 +- .../odeme/components/paket-satinal.tsx | 179 ++++++++++-------- 2 files changed, 104 insertions(+), 86 deletions(-) diff --git a/src/app/paket/page.tsx b/src/app/paket/page.tsx index 3d8131f..a6acc93 100644 --- a/src/app/paket/page.tsx +++ b/src/app/paket/page.tsx @@ -4,10 +4,7 @@ import type { Metadata } from "next"; import { SectionEyebrow } from "@/components/pixel-decor"; import { Parallax } from "@/components/parallax"; import { SiteFooter } from "@/components/site-footer"; -import { - PaketSatinAl, - PaketSatinAlSkeleton, -} from "@/features/odeme/components/paket-satinal"; +import { PaketSatinAl } from "@/features/odeme/components/paket-satinal"; import { sayfaMetadata } from "@/lib/seo"; export const metadata: Metadata = sayfaMetadata({ @@ -57,9 +54,9 @@ export default function PaketPage({

- }> - - + {/* Kart gövdesi statik kabukta; yalnız CTA alanları içeride kendi + Suspense sınırlarıyla istek anında gelir (bkz. paket-satinal.tsx). */} +

KolayTercih bir karar destek aracıdır; öneriler resmî YÖK Atlas diff --git a/src/features/odeme/components/paket-satinal.tsx b/src/features/odeme/components/paket-satinal.tsx index 5ebf077..d5cb8d6 100644 --- a/src/features/odeme/components/paket-satinal.tsx +++ b/src/features/odeme/components/paket-satinal.tsx @@ -1,3 +1,4 @@ +import { Suspense } from "react"; import Image from "next/image"; import Link from "next/link"; import { @@ -11,7 +12,8 @@ import { ShieldCheck, } from "lucide-react"; import { getCurrentUser } from "@/lib/session"; -import { URUNLER, DENEME_KREDISI } from "@/lib/credits"; +import { URUNLER, DENEME_KREDISI, RAPOR_KREDI } from "@/lib/credits"; +import { ACIK_SATIR } from "@/lib/rapor-maske"; import { VERI_YIL_SAYISI } from "@/lib/veri-kapsami"; import { Button } from "@/components/ui/button"; import { Parallax } from "@/components/parallax"; @@ -39,11 +41,17 @@ const OZELLIKLER = [ }, ] as const; -// Paket + top-up kartları: kullanıcının paket/kredi durumuna göre CTA'lar -// değişir, okuma burada yaşar. -export async function PaketSatinAl() { - const u = await getCurrentUser(); - +// Paket + top-up kartları. Kartların gövdesi (başlık, fiyat, özellikler, +// iyzico bandı) STATİK kabukta kalır; kullanıcıya bağlı olan yalnızca iki küçük +// CTA alanıdır (PaketCta, TopupCta) ve her biri kendi Suspense sınırındadır. +// +// Neden bu kadar küçük parçalar: React Flight ~3,2 KB'ı aşan bir dinamik +// parçanın devamını ayrı satırlara böler; PPR resume bunlara yeni segment +// kimliği verirken kabukta geç tamamlanan sınırın (kök loading.tsx) kimliğiyle +// çakışıyor (Next 16.2 / React 19.2) ve tarayıcıda HierarchyRequestError ile +// sayfa boş kalıyordu. Dinamik parçayı bu sınırın altında tut: kartın gövdesini +// yeniden bu async bileşenlerin içine TAŞIMA. +export function PaketSatinAl() { return (

- {u ? ( - u.hasPaket ? ( -
- - Paketin aktif —{" "} - {/* ?uret=1: rapor varsa /listem raporu gösterir (bayrak yok - sayılır), yoksa bekleyen seçimlerle üretim başlar; seçim - de yoksa boş durum sihirbaza yönlendirir. */} - - listeni oluştur - -
- ) : ( - - ) - ) : ( -
- -

- Girişte {DENEME_KREDISI} deneme kredisi de tanımlanır. -

-
- )} + }> + +
- {/* Top-up hasPaket vermez: paketsiz biri alırsa parasını verip - yine maskeli liste görür. Satın alma yalnızca paketliye - açık; paketsize durum dürüstçe açıklanır. */} - {u?.hasPaket ? ( - - ) : u ? ( -

- Ek kredi, kilitli analizleri açmaz — o yüzden önce paket - gerekir. Paket zaten {URUNLER.paket.credits} kredi içerir; - bitince buradan yükleyebilirsin. -

- ) : ( - - )} + }> + +
); } -export function PaketSatinAlSkeleton() { +/** Ana paket kartının kullanıcıya bağlı CTA'sı — bilerek küçük (bkz. üstteki not). */ +async function PaketCta() { + const u = await getCurrentUser(); + + if (!u) { + return ( +
+ +

+ Girişte {DENEME_KREDISI} deneme kredisi tanımlanır; liste{" "} + {RAPOR_KREDI} kredi, ilk {ACIK_SATIR} satır açık. Paketi sonra + alırsın. +

+
+ ); + } + + if (u.hasPaket) { + return ( +
+ + Paketin aktif —{" "} + {/* ?uret=1: rapor varsa /listem raporu gösterir (bayrak yok + sayılır), yoksa bekleyen seçimlerle üretim başlar; seçim + de yoksa boş durum sihirbaza yönlendirir. */} + + listeni oluştur + +
+ ); + } + return ( -
- - -
+ + ); +} + +/** Top-up kartının kullanıcıya bağlı CTA'sı — bilerek küçük (bkz. üstteki not). */ +async function TopupCta() { + const u = await getCurrentUser(); + + // Top-up hasPaket vermez: paketsiz biri alırsa parasını verip yine maskeli + // liste görür. Satın alma yalnızca paketliye açık; paketsize durum dürüstçe + // açıklanır. + if (u?.hasPaket) { + return ( + + ); + } + + if (u) { + return ( +

+ Ek kredi, kilitli analizleri açmaz — o yüzden önce paket gerekir. + Paket zaten {URUNLER.paket.credits} kredi içerir; bitince buradan + yükleyebilirsin. +

+ ); + } + + return ( + ); }