merge: origin/gece/p2-olcum~1 (gece vardiyası 2026-09-21)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -38,8 +38,10 @@ export function GirisForm({ callbackURL }: { callbackURL: string }) {
|
||||
// Yeniden gönderim arası bekleme (sn) — istemeden spam'lenmesin
|
||||
const [bekleme, setBekleme] = useState(0);
|
||||
|
||||
// 60 sn: sunucunun adres başına gönderim soğumasıyla hizalı; daha kısa
|
||||
// sayaçta "Yeniden gönder" sessizce yutulur, kullanıcı gelmeyen maili bekler.
|
||||
function geriSayimBaslat() {
|
||||
setBekleme(30);
|
||||
setBekleme(60);
|
||||
const sayac = setInterval(() => {
|
||||
setBekleme((s) => {
|
||||
if (s <= 1) {
|
||||
@@ -59,6 +61,7 @@ export function GirisForm({ callbackURL }: { callbackURL: string }) {
|
||||
callbackURL,
|
||||
});
|
||||
if (error) {
|
||||
olay("giris_hata", { yontem: "google", status: error.status ?? 0 });
|
||||
toast.error("Google ile giriş başarısız. Tekrar dener misin?");
|
||||
setBusy(null);
|
||||
}
|
||||
@@ -81,7 +84,13 @@ export function GirisForm({ callbackURL }: { callbackURL: string }) {
|
||||
});
|
||||
setBusy(null);
|
||||
if (error) {
|
||||
toast.error("Bağlantı gönderilemedi. Tekrar dener misin?");
|
||||
olay("giris_hata", { yontem: "eposta", status: error.status ?? 0 });
|
||||
// 429 = hız sınırı: "tekrar dene" demek sınırı daha da uzatır
|
||||
toast.error(
|
||||
error.status === 429
|
||||
? "Çok sık denedin, bir dakika sonra tekrar dene"
|
||||
: "Bağlantı gönderilemedi. Tekrar dener misin?",
|
||||
);
|
||||
return;
|
||||
}
|
||||
setSent(true);
|
||||
|
||||
@@ -48,7 +48,7 @@ export async function UserNav() {
|
||||
<ListeSenkron userId={u.id} />
|
||||
<ListemButonu />
|
||||
<Link
|
||||
href="/paket"
|
||||
href="/paket?kaynak=nav"
|
||||
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"
|
||||
>
|
||||
|
||||
@@ -163,6 +163,7 @@ function KapıIcerik({
|
||||
sira_kovasi: siraKovasi(siraDeger),
|
||||
});
|
||||
} catch {
|
||||
olay("sira_form_hata", { kaynak: duzenle ? "duzenle" : "kapi" });
|
||||
setHata("Bir şeyler ters gitti, tekrar dener misin?");
|
||||
} finally {
|
||||
setYukleniyor(false);
|
||||
|
||||
25
src/features/odeme/components/paket-gorunum-olayi.tsx
Normal file
25
src/features/odeme/components/paket-gorunum-olayi.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
"use client";
|
||||
|
||||
// /paket görüntülenmesini ve ziyaretçiyi oraya getiren yüzeyi ölçer.
|
||||
//
|
||||
// Neden ayrı bileşen + layout: /paket sayfası sunucu bileşeni ve statik
|
||||
// kalmalı. `useSearchParams` kullanılmaz — cacheComponents açıkken Suspense
|
||||
// sınırı/CSR bailout gerektirir; parametre yalnızca ölçüm için lazım, mount
|
||||
// sonrası window.location'dan okumak yeter.
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { olayBirKez } from "@/lib/analitik";
|
||||
|
||||
// Serbest metin analitiğe sızmasın: bilinmeyen değerler "diger"e düşer.
|
||||
const KAYNAKLAR = ["kilit", "kredi_bitti", "nav"] as const;
|
||||
|
||||
export function PaketGorunumOlayi() {
|
||||
useEffect(() => {
|
||||
const ham = new URLSearchParams(window.location.search).get("kaynak");
|
||||
const kaynak = KAYNAKLAR.find((k) => k === ham) ?? "diger";
|
||||
// Oturum başına kaynak başına 1 kez: yenileme sayıyı şişirmesin
|
||||
olayBirKez("paket_goruntulendi", { kaynak }, kaynak);
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -84,9 +84,11 @@ export function ListeUretici({
|
||||
} else if (sonuc.code === "AUTH") {
|
||||
router.push(`/giris?callback=${encodeURIComponent("/listem?uret=1")}`);
|
||||
} else {
|
||||
olay("liste_uretim_hata", { kod: sonuc.code });
|
||||
setDurum({ ad: "hata", mesaj: sonuc.error });
|
||||
}
|
||||
} catch {
|
||||
olay("liste_uretim_hata", { kod: "ISTISNA" });
|
||||
setDurum({
|
||||
ad: "hata",
|
||||
mesaj: "Beklenmeyen bir hata oluştu, tekrar dener misin?",
|
||||
@@ -131,7 +133,7 @@ export function ListeUretici({
|
||||
size="lg"
|
||||
className="mt-6 cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
|
||||
>
|
||||
<Link href="/paket">
|
||||
<Link href="/paket?kaynak=kredi_bitti">
|
||||
{hasPaket ? "+30 kredi yükle — 129 TL" : "Paketi aktive et — 299 TL"}
|
||||
<ArrowRight className="size-4" aria-hidden />
|
||||
</Link>
|
||||
|
||||
@@ -285,7 +285,7 @@ export function ListemGovde({
|
||||
asChild
|
||||
className="h-11 w-full shrink-0 cursor-pointer bg-orange-500 text-white transition-[background-color,transform] duration-200 active:scale-[0.97] hover:bg-orange-600 sm:w-auto"
|
||||
>
|
||||
<Link href="/paket">
|
||||
<Link href="/paket?kaynak=kilit">
|
||||
Analizleri aç — {paketFiyati} TL
|
||||
<ArrowRight data-icon="inline-end" aria-hidden />
|
||||
</Link>
|
||||
@@ -366,7 +366,7 @@ export function ListemGovde({
|
||||
size="lg"
|
||||
className="mt-5 w-full cursor-pointer bg-orange-500 text-white transition-[background-color,transform] duration-200 active:scale-[0.97] hover:bg-orange-600"
|
||||
>
|
||||
<Link href="/paket">
|
||||
<Link href="/paket?kaynak=kilit">
|
||||
Kişisel analizi aç
|
||||
<ArrowRight data-icon="inline-end" aria-hidden />
|
||||
</Link>
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
DrawerTitle,
|
||||
} from "@/components/ui/drawer";
|
||||
import { SectionEyebrow } from "@/components/pixel-decor";
|
||||
import { useGorunumOlayi } from "@/components/use-gorunum-olayi";
|
||||
import {
|
||||
SihirbazAdimlarLazy,
|
||||
preloadSihirbazAdimlar,
|
||||
@@ -73,6 +74,10 @@ export function CtaSiraForm({ baslik }: { baslik?: string }) {
|
||||
const [yukleniyor, setYukleniyor] = useState(false);
|
||||
const [hata, setHata] = useState<string | null>(null);
|
||||
const [girisli, setGirisli] = useState(false);
|
||||
// Huni girişi ölçümü: ref yalnızca profilsiz daldaki karta bağlanır; kayıtlı
|
||||
// profilli ziyaretçi formu hiç görmediğinden sayılmaz. Sayfa ayrımı için
|
||||
// özellik gerekmez — Rybbit olayın pathname'ini zaten yazar.
|
||||
const formRef = useGorunumOlayi<HTMLElement>("cta_form_goruntulendi");
|
||||
|
||||
// Yazarken binlik ayracıyla biçimle (85000 → 85.000)
|
||||
function siralamaDegisti(ham: string) {
|
||||
@@ -108,6 +113,7 @@ export function CtaSiraForm({ baslik }: { baslik?: string }) {
|
||||
sira_kovasi: siraKovasi(value),
|
||||
});
|
||||
} catch {
|
||||
olay("sira_form_hata", { kaynak: "cta" });
|
||||
setHata("Bir şeyler ters gitti, tekrar dener misin?");
|
||||
} finally {
|
||||
setYukleniyor(false);
|
||||
@@ -159,7 +165,10 @@ export function CtaSiraForm({ baslik }: { baslik?: string }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="rounded-2xl border border-primary/20 bg-primary/5 p-6 sm:p-8">
|
||||
<section
|
||||
ref={formRef}
|
||||
className="rounded-2xl border border-primary/20 bg-primary/5 p-6 sm:p-8"
|
||||
>
|
||||
<h2 className="font-heading text-xl font-bold text-slate-900">
|
||||
{baslik ?? "Sıralamanla nereye yerleşirsin? Saniyede gör."}
|
||||
</h2>
|
||||
|
||||
@@ -85,6 +85,7 @@ export function HeroForm() {
|
||||
sira_kovasi: siraKovasi(value),
|
||||
});
|
||||
} catch {
|
||||
olay("sira_form_hata", { kaynak: "hero" });
|
||||
setHata("Bir şeyler ters gitti, tekrar dener misin?");
|
||||
} finally {
|
||||
setYukleniyor(false);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useReducedMotion } from "motion/react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PixelField } from "@/components/pixel-decor";
|
||||
import { useGorunumOlayi } from "@/components/use-gorunum-olayi";
|
||||
import { olay } from "@/lib/analitik";
|
||||
import { olay, olayBirKez, siraKovasi } from "@/lib/analitik";
|
||||
import type { SihirbazFacetleri } from "@/types/yokatlas";
|
||||
import { SIHIRBAZ_AC_EVENT, SIHIRBAZ_STORAGE_KEY, sihirbazDogrula, type SihirbazSecimleri } from "@/features/sihirbaz/sihirbaz-sabitler";
|
||||
import { tercihProfiliYaz } from "@/features/sihirbaz/sihirbaz-profil";
|
||||
@@ -82,6 +82,22 @@ export function SihirbazBolumu({
|
||||
// görsün diye ref'te tutulur; olay dinleyicisi bir kez bağlanır.
|
||||
const bekleyenRef = useRef<BekleyenSecim | null>(null);
|
||||
|
||||
// Huni adımı: sonuç sayfası görüntülendi. URL sıralamayı taşıdığı için
|
||||
// pathname maskeli (bkz. app/layout.tsx); huni bu olayla kurulur. Oturum
|
||||
// başına girişli/anonim için birer kez — girişten dönen ziyaretçi ikinci
|
||||
// kez "girisli" olarak sayılır. Ham sıra gönderilmez, yalnızca kovası.
|
||||
useEffect(() => {
|
||||
olayBirKez(
|
||||
"sonuc_goruntulendi",
|
||||
{
|
||||
girisli: girisliMi ? "evet" : "hayir",
|
||||
sira_kovasi: siraKovasi(sira),
|
||||
},
|
||||
girisliMi ? "girisli" : "anonim",
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// Mount sonrası storage'dan bekleyen seçimleri oku
|
||||
useEffect(() => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user