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

@@ -0,0 +1,122 @@
import Link from "next/link";
import { redirect } from "next/navigation";
import { getSession } from "@/lib/session";
import { PixelCorner } from "@/components/pixel-decor";
import { Skeleton } from "@/components/ui/skeleton";
import { GirisForm } from "./giris-form";
import { GirisBaslik } from "./giris-baslik";
export async function GirisKarti({
callback,
hata,
}: {
callback?: string;
hata?: string;
}) {
const callbackURL = callback && callback.startsWith("/") ? callback : "/";
// Magic link doğrulaması başarısız olunca errorCallbackURL buraya düşürür
// (bkz. giris-form.tsx ve /giris/dogrula) — ham INVALID_TOKEN yerine
// dostça bir mesaj gösterip yeni bağlantı istemeye yönlendiriyoruz.
const baglantiHatasi = hata === "gecersiz-baglanti";
const session = await getSession();
if (session) redirect(callbackURL);
// Sihirbaz funnel'ı: seçimler yapıldı, listeye giriş kaldı
const funnel = callbackURL.startsWith("/sonuc");
return (
<>
<div className="relative">
<div className="relative rounded-2xl border border-slate-200 bg-white p-8 shadow-sm">
{baglantiHatasi && (
<div
role="alert"
className="mb-5 rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-800"
>
<p className="font-semibold">
Giriş bağlantısı geçersiz ya da süresi dolmuş
</p>
<p className="mt-1">
Bağlantılar 15 dakika geçerli ve tek kullanımlık. Aşağıdan
yeni bir bağlantı isteyebilirsin.
</p>
</div>
)}
<GirisBaslik funnel={funnel} />
<p className="mt-2 text-sm text-slate-600">
{funnel ? (
<>
Seçimlerin hazır — giriş yap,{" "}
<span className="font-semibold">5 deneme kredinle</span> listen
hemen üretilsin.
</>
) : (
<>
Kayıt olduğunda{" "}
<span className="font-semibold">5 deneme kredisi</span>{" "}
hesabına tanımlanır — Yapay Zeka danışmana ücretsiz soru sorabilirsin.
</>
)}
</p>
<GirisForm callbackURL={callbackURL} />
</div>
<div
aria-hidden
className="pointer-events-none absolute inset-0 z-10 text-primary"
>
<PixelCorner className="absolute -right-4 -top-4 size-9 [&_rect]:[fill-opacity:0.65]" />
<PixelCorner className="absolute -bottom-4 -right-4 size-9 rotate-90 [&_rect]:[fill-opacity:0.65]" />
<PixelCorner className="absolute -bottom-4 -left-4 size-9 rotate-180 [&_rect]:[fill-opacity:0.65]" />
<PixelCorner className="absolute -left-4 -top-4 size-9 -rotate-90 [&_rect]:[fill-opacity:0.65]" />
</div>
</div>
<p className="mt-6 text-center text-xs leading-relaxed text-slate-500">
Giriş yaparak verilerinin tercih önerileri üretmek amacıyla{" "}
<Link href="/gizlilik" className="underline hover:text-slate-700">
gizlilik metnindeki
</Link>{" "}
gibi işlenmesini ve{" "}
<Link href="/kosullar" className="underline hover:text-slate-700">
kullanım koşullarını
</Link>{" "}
kabul etmiş olursun. KolayTercih yerleşme garantisi vermez.
</p>
</>
);
}
// Kart, başlık, Google butonu, ayraç, e-posta alanı ve gönder butonu aynı
// yerde belirir — içerik gelince zıplama olmaz.
export function GirisKartiSkeleton() {
return (
<>
<div className="rounded-2xl border border-slate-200 bg-white p-8 shadow-sm">
{/* Başlık + alt metin */}
<Skeleton className="h-8 w-40" />
<Skeleton className="mt-3 h-4 w-full" />
<Skeleton className="mt-1.5 h-4 w-2/3" />
{/* Google ile devam et */}
<Skeleton className="mt-6 h-11 w-full rounded-lg" />
{/* "veya" ayracı */}
<div className="mt-4 flex items-center gap-3">
<Skeleton className="h-px flex-1" />
<Skeleton className="h-3 w-8" />
<Skeleton className="h-px flex-1" />
</div>
{/* E-posta alanı + gönder butonu */}
<Skeleton className="mt-4 h-11 w-full rounded-lg" />
<Skeleton className="mt-3 h-11 w-full rounded-lg" />
</div>
{/* Kart altındaki yasal metin */}
<div className="mt-6 flex flex-col items-center gap-1.5">
<Skeleton className="h-3 w-3/4" />
<Skeleton className="h-3 w-1/2" />
</div>
</>
);
}