refactor: kullanici feature'ı — giriş kartları feature bileşenlerine çıktı (Faz 4)

- giris-form, giris-baslik, user-nav, sign-out-button
  features/kullanici/components/ altına taşındı
- /giris'in session+redirect+callback temizliği GirisKarti'ya (async),
  /giris/dogrula'nın inline doğrulama formu + guvenliYol kalkanı
  DogrulaKarti'ya çıkarıldı; skeleton'lar aynı dosyada
- İki sayfa senkron: searchParams.then() + sayfada Suspense;
  app/giris/loading.tsx silindi (geometrisi GirisKartiSkeleton'da yaşıyor)
- site-header'ın UserNav importu güncellendi; mevcut Suspense sınırı yerinde
- Davranış değişikliği yok

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-08-08 01:22:31 +03:00
parent e3cf141f55
commit 8b5aa8c8a6
10 changed files with 254 additions and 195 deletions

View File

@@ -1,24 +0,0 @@
"use client";
import { useRouter } from "next/navigation";
import { LogOut } from "lucide-react";
import { authClient } from "@/lib/auth-client";
import { Button } from "@/components/ui/button";
export function SignOutButton() {
const router = useRouter();
return (
<Button
size="sm"
variant="ghost"
className="size-9 cursor-pointer rounded-full border border-slate-200 bg-white text-slate-600 transition-colors duration-200 hover:border-slate-300 hover:bg-slate-50 hover:text-slate-900 sm:size-12"
aria-label="Çıkış yap"
onClick={async () => {
await authClient.signOut();
router.refresh();
}}
>
<LogOut className="size-4 sm:size-5" aria-hidden />
</Button>
);
}

View File

@@ -2,7 +2,7 @@ import { Suspense } from "react";
import Image from "next/image";
import Link from "next/link";
import { KatalogArama } from "@/features/katalog/components/katalog-arama";
import { UserNav, UserNavFallback } from "@/components/user-nav";
import { UserNav, UserNavFallback } from "@/features/kullanici/components/user-nav";
import { getAllRehberler } from "@/lib/rehber";
export function SiteHeader() {

View File

@@ -1,65 +0,0 @@
import Link from "next/link";
import { Coins } from "lucide-react";
import { getCurrentUser } from "@/lib/session";
import { Button } from "@/components/ui/button";
import { ListemButonu } from "@/components/manuel-liste/listem-butonu";
import { ListeSenkron } from "@/components/manuel-liste/liste-senkron";
import { RybbitIdentify } from "@/components/rybbit-identify";
import { SignOutButton } from "./sign-out-button";
/**
* Suspense fallback'i: girişsiz görünümle aynı yükseklik/genişlikte sessiz
* bir pill — session sorgusu akarken header zıplamasın (CLS yok).
*/
export function UserNavFallback() {
return (
<div
className="order-2 flex shrink-0 items-center gap-1.5 sm:gap-2"
aria-hidden
>
<div className="h-9 w-16 rounded-full bg-slate-900/10 sm:h-12 sm:w-28" />
</div>
);
}
export async function UserNav() {
const u = await getCurrentUser();
// Manuel liste localStorage'ta yaşadığı için Listem girişsiz de çalışır
if (!u) {
return (
<div className="order-2 flex shrink-0 items-center gap-1.5 sm:gap-2">
<RybbitIdentify kullaniciId={null} />
<ListeSenkron userId={null} />
<ListemButonu />
<Button
asChild
className="h-9 cursor-pointer rounded-full bg-slate-900 px-3.5 text-sm font-medium text-white transition-colors duration-200 hover:bg-slate-700 sm:h-12 sm:px-7 sm:text-base"
>
<Link href="/giris">Giriş yap</Link>
</Button>
</div>
);
}
return (
<div className="order-2 flex shrink-0 items-center gap-1.5 sm:gap-2">
<RybbitIdentify kullaniciId={u.id} eposta={u.email} isim={u.name} />
<ListeSenkron userId={u.id} />
<ListemButonu />
<Link
href="/paket"
className="inline-flex h-9 items-center gap-1.5 whitespace-nowrap rounded-full border border-slate-200 bg-white px-3 text-xs font-semibold text-slate-700 transition-colors duration-200 hover:border-slate-300 hover:bg-slate-50 sm:h-12 sm:gap-2 sm:px-5 sm:text-sm"
title="Kredilerin — yüklemek için tıkla"
>
<Coins className="size-3.5 text-amber-500 sm:size-4" aria-hidden />
<span className="tabular-nums">{u.creditBalance}</span>
{/* Etiket yalnızca md–lg arası: daha darda logo kesiliyor, lg üstünde
ortadaki nav ile yer dar (konteyner 1152'de sabit). İkon+sayı ve
title her genişlikte anlamı taşıyor. */}
<span className="hidden md:inline lg:hidden">kredi</span>
</Link>
<SignOutButton />
</div>
);
}