Files
kolaytercih/src/features/odeme/components/satin-al-form.tsx
bilalgursen 3c995402c6 feat(ui): Arc bileşenleri, landing bölümleri ve bildirim yığını
Sonner/vaul yerine kendi bildirim yığını ve alt sayfa; çip grubu, kart
destesi, kaydırma eylemi, yazı dönüşümü, bölme tutamacı; landing'e program
duvarı, süzülen haplar ve veri akışı; yaylar tek dosyada (lib/hareket.ts).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-07 17:12:53 +03:00

87 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useLayoutEffect, useState, useTransition } from "react";
import { useRouter } from "next/navigation";
import { ArrowRight, Loader2 } from "lucide-react";
import { toast } from "@/components/ui/bildirim";
import { Button } from "@/components/ui/button";
import { olay } from "@/lib/analitik";
import { baslatOdeme } from "../odeme-actions";
const BAGLANTI_HATASI =
"Ödeme başlatılamadı; kartından çekim yapılmadı. Bağlantını kontrol edip tekrar dener misin?";
export function SatinAlForm({
urun,
label,
vurgulu = false,
}: {
urun: "paket" | "topup";
label: string;
vurgulu?: boolean;
}) {
const router = useRouter();
const [pending, startTransition] = useTransition();
// Action bitti, /odeme'ye geçiş sürüyor: buton o arada yeniden açılıp ikinci
// bir sipariş başlatmasın.
const [gidiyor, setGidiyor] = useState(false);
const mesgul = pending || gidiyor;
// cacheComponents açıkken /paket, /odeme'ye geçince unmount olmaz; gizli
// <Activity> içinde state'iyle saklanır. Geri dönen kullanıcı kilitli değil
// taze bir buton bulsun diye sayfa gizlenirken sıfırlanır (layout effect
// cleanup'ı Activity gizlenince çalışır).
useLayoutEffect(() => {
return () => setGidiyor(false);
}, []);
// Yönlendirme client'ta: action `redirect()` fırlatmaz, hedefi döndürür
// (nedeni odeme-actions.ts'te). Hata toast'ı da burada, effect'te değil —
// effect'ler Activity geri açılınca yeniden koştuğu için eski hatayı
// tekrar gösteriyordu.
function gonder() {
olay("odeme_baslatildi", { urun });
startTransition(async () => {
let sonuc;
try {
sonuc = await baslatOdeme(urun);
} catch {
toast.error(BAGLANTI_HATASI);
return;
}
if ("error" in sonuc) {
toast.error(sonuc.error);
return;
}
setGidiyor(true);
router.push(sonuc.yonlendir);
});
}
return (
<form action={gonder}>
<Button
type="submit"
disabled={mesgul}
aria-busy={mesgul}
className={
vurgulu
? "h-12 w-full cursor-pointer bg-orange-500 px-8 text-white transition-colors duration-200 hover:bg-orange-600"
: "h-11 w-full cursor-pointer"
}
variant={vurgulu ? "default" : "outline"}
>
{/* Tıklamada metin değişmez (dar top-up kartında taşıyordu); geri
bildirim aynı boyuttaki ikon takasıyla verilir: ok → dönen halka.
Çift gönderim `disabled` ile engellenir. */}
{label}
{mesgul ? (
<Loader2 className="size-4 animate-spin" aria-hidden />
) : (
<ArrowRight className="size-4" aria-hidden />
)}
</Button>
</form>
);
}