UX/CRO denetimi uygulandı: değer-önce funnel, güven sayfaları, dev paneli

Kritik (K1-K6) sonrası kalan tüm maddeler:
- Değer önce: girişsiz kullanıcı sihirbaz bitince /giris yerine /sonuc'taki
  ücretsiz tabloya gider (?hazir=1); AI CTA'sı "5 deneme kredinle listeni
  kur — giriş yap" paneli + sabit alt karttan verilir, girişte otomatik üretim
- Ö1-Ö10: paywall kopyası, aranabilir il seçimi, "Seçtiklerim" adlandırması,
  gizlilik/koşullar/iletişim sayfaları, danışman özeti + uyarılar, revizyon
  kutusu, dilim özeti + tek şehir uyarısı, eksik veri rozetleri, magic link
  yeniden gönder, mobil sohbet kısayolu
- G1-G5: binlik ayraçlı input + inline hata, adım göstergesi, route-level
  loading/error/not-found, dürüst konumlandırma kartı, dürüst buton etiketleri
- Seçtiklerim butonu liste boşken gizli; ilk eklemede belirir
- Dev süperadmin paneli (yalnızca development): hızlı giriş, kredi/paket/rapor
  senaryoları, sahte ödeme dönüşü

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-07-26 15:10:57 +03:00
parent d93cf742ff
commit 7288c926d0
40 changed files with 1666 additions and 162 deletions

View File

@@ -34,6 +34,21 @@ export function GirisForm({ callbackURL }: { callbackURL: string }) {
const [email, setEmail] = useState("");
const [busy, setBusy] = useState<"google" | "email" | null>(null);
const [sent, setSent] = useState(false);
// Yeniden gönderim arası bekleme (sn) — istemeden spam'lenmesin
const [bekleme, setBekleme] = useState(0);
function geriSayimBaslat() {
setBekleme(30);
const sayac = setInterval(() => {
setBekleme((s) => {
if (s <= 1) {
clearInterval(sayac);
return 0;
}
return s - 1;
});
}, 1000);
}
async function googleIleGiris() {
setBusy("google");
@@ -47,8 +62,8 @@ export function GirisForm({ callbackURL }: { callbackURL: string }) {
}
}
async function linkGonder(e: React.FormEvent) {
e.preventDefault();
async function linkGonder(e?: React.FormEvent) {
e?.preventDefault();
if (!/^\S+@\S+\.\S+$/.test(email)) {
toast.error("Geçerli bir e-posta adresi gir.");
return;
@@ -64,17 +79,41 @@ export function GirisForm({ callbackURL }: { callbackURL: string }) {
return;
}
setSent(true);
geriSayimBaslat();
}
if (sent) {
return (
<div className="mt-6 rounded-xl border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-800">
<p className="font-semibold">Bağlantı gönderildi 📬</p>
<p className="mt-1">
<span className="font-medium">{email}</span> adresine bir giriş
bağlantısı gönderdik. Gelen kutunu (ve spam klasörünü) kontrol et
bağlantı 5 dakika geçerli.
</p>
<div className="mt-6 space-y-3">
<div className="rounded-xl border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-800">
<p className="font-semibold">Bağlantı gönderildi 📬</p>
<p className="mt-1">
<span className="font-medium">{email}</span> adresine bir giriş
bağlantısı gönderdik. Gelen kutunu (ve spam klasörünü) kontrol et
bağlantı 5 dakika geçerli.
</p>
</div>
<div className="flex items-center justify-center gap-4 text-sm">
<button
type="button"
onClick={() => linkGonder()}
disabled={busy !== null || bekleme > 0}
className="cursor-pointer font-medium text-primary underline-offset-2 hover:underline disabled:cursor-default disabled:text-slate-400 disabled:no-underline"
>
{busy === "email"
? "Gönderiliyor…"
: bekleme > 0
? `Yeniden gönder (${bekleme})`
: "Yeniden gönder"}
</button>
<button
type="button"
onClick={() => setSent(false)}
className="cursor-pointer text-slate-500 underline-offset-2 hover:underline"
>
E-postayı düzelt
</button>
</div>
</div>
);
}