"use client"; // Dev süperadmin paneli: yalnızca geliştirme ortamında render edilir // (layout.tsx NODE_ENV kontrolüyle monte eder; aksiyonlar da sunucuda // ayrıca korunur). Amaç: satın alma / giriş / kredi durumlarını tek // tıkla taklit edip uygulamanın tüm sayfalarını gerçek akışta gezmek. // // Hareket dili (Apple "Designing Fluid Interfaces"): panel DEV çipinden // büyür ve yine ona küçülür (paylaşılan layoutId ile mekânsal bağ), yay // kritik sönümlüdür (bounce 0) — açılış ortasında kapatılırsa mevcut // değerden geri döner. Yüzey opak kutu değil, yarı saydam malzemedir. import { useCallback, useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import { Loader2, Wrench, X } from "lucide-react"; import { AnimatePresence, motion, useReducedMotion } from "motion/react"; import { toast } from "sonner"; import { authClient } from "@/lib/auth-client"; import { devAnindaGiris, devAyarla, devDurum, devRaporSil, devSahteOdeme, type DevDurum, } from "@/lib/dev/actions"; const SAYFALAR: { ad: string; href: string }[] = [ { ad: "Ana sayfa", href: "/" }, { ad: "Sonuç (85k · SAY)", href: "/sonuc?sira=85000&tur=say&sihirbaz=1" }, { ad: "Sonuç (girişsiz hazır)", href: "/sonuc?sira=85000&tur=say&hazir=1" }, { ad: "Listem", href: "/listem" }, { ad: "Listem (kilit açılışı)", href: "/listem?acildi=1" }, { ad: "Paket", href: "/paket" }, { ad: "Paket (ödeme hatası)", href: "/paket?hata=token" }, { ad: "PDF / yazdır", href: "/rapor/yazdir" }, { ad: "Giriş", href: "/giris" }, { ad: "Gizlilik", href: "/gizlilik" }, { ad: "Koşullar", href: "/kosullar" }, { ad: "İletişim", href: "/iletisim" }, { ad: "404", href: "/boyle-bir-sayfa-yok" }, ]; // Apple değerleri: damping 1.0 (bounce 0), response ~0.35 sn. // Yay süreli değildir; hedef değişirse mevcut değer + hızdan devam eder. const YAY = { type: "spring", bounce: 0, duration: 0.35 } as const; // Basış anında tepki (pointer-down feedback) — bırakınca değil. const BASIS = "active:scale-[0.97] transition-[transform,background-color,border-color,opacity] duration-150 ease-out"; // Yarı saydam malzeme; şeffaflık azaltılsın diyen kullanıcıya opak yüzey. const MALZEME = "border border-white/60 bg-white/80 shadow-2xl shadow-slate-900/20 backdrop-blur-2xl backdrop-saturate-150 " + "[@media(prefers-reduced-transparency:reduce)]:bg-white [@media(prefers-reduced-transparency:reduce)]:backdrop-blur-none"; export function DevPanel() { const [acik, setAcik] = useState(false); const [durum, setDurum] = useState(null); const [email, setEmail] = useState("test@kolaytercih.dev"); const [mesgul, setMesgul] = useState(false); const router = useRouter(); const azMotion = useReducedMotion(); const yenile = useCallback(async () => { try { setDurum(await devDurum()); } catch { setDurum(null); } }, []); function paneliAc() { setAcik(true); void yenile(); } // Yol bulma: panelden çıkış her an mümkün — Escape da kapatır. useEffect(() => { if (!acik) return; const f = (e: KeyboardEvent) => { if (e.key === "Escape") setAcik(false); }; window.addEventListener("keydown", f); return () => window.removeEventListener("keydown", f); }, [acik]); // Her aksiyonu tek kalıptan geçir: meşgul kilidi + durum tazeleme + // server component'lerin (header'daki kredi rozeti dahil) yenilenmesi async function calistir(fn: () => Promise) { setMesgul(true); try { await fn(); await yenile(); router.refresh(); } catch (e) { toast.error(e instanceof Error ? e.message : "Dev aksiyonu başarısız."); } finally { setMesgul(false); } } async function anindaGiris() { setMesgul(true); try { const sonuc = await devAnindaGiris(email); if (!sonuc.ok) { toast.error(sonuc.error); return; } // Verify URL'i oturumu kurup callback'e yönlendirir window.location.assign(sonuc.url); } finally { setMesgul(false); } } async function odemeEkrani() { setMesgul(true); try { const sonuc = await devSahteOdeme(); if (!sonuc.ok) { toast.error(sonuc.error); return; } router.push(`/odeme/sonuc?siparis=${sonuc.siparis}`); } finally { setMesgul(false); } } const g = durum?.girisli ? durum : null; // Azaltılmış hareket: morph yerine kısa çapraz solma (yaysız, kaymasız). const paylasilan = azMotion ? {} : { layoutId: "dev-panel" }; const gecis = azMotion ? { duration: 0.15 } : YAY; return ( {!acik ? ( DEV ) : ( {/* İçerik morph sırasında bozulmasın: yüzey oturduktan hemen sonra belirir (malzeme önce, içerik sonra). */} {/* Başlık: opak şerit değil, aynı malzemenin üstünde ince bir kenar çizgisiyle ayrışan katman. */}

Dev süperadmin {mesgul ? ( ) : null}

{/* Oturum */}

Oturum

{g ? (

{g.email}

{g.kredi} kredi · {g.hasPaket ? "paketli" : "paketsiz"} ·{" "} {g.raporVar ? `rapor var (${g.sira?.toLocaleString("tr-TR")} · ${g.tur})` : "rapor yok"}

) : (

Girişsiz geziyorsun.

)}
setEmail(e.target.value)} placeholder="deneme@ornek.dev" className="h-9 min-w-0 flex-1 rounded-lg border border-slate-900/10 bg-white/70 px-2.5 text-xs outline-none transition-colors duration-150 focus-visible:border-violet-400" />

Yeni e-posta = 5 kredili yeni kullanıcı (magic link atlanır).

{/* Senaryolar */}

Senaryo

{( [ { ad: "Yeni kullanıcı", aciklama: "5 kredi · paketsiz · raporsuz", fn: async () => { await devAyarla({ kredi: 5, hasPaket: false }); await devRaporSil(); }, }, { ad: "Paketli", aciklama: "60 kredi · paket aktif", fn: () => devAyarla({ kredi: 60, hasPaket: true }), }, { ad: "Kredisi bitmiş", aciklama: "0 kredi", fn: () => devAyarla({ kredi: 0 }), }, { ad: "Raporu sil", aciklama: "boş durumlar", fn: () => devRaporSil(), }, ] as const ).map((s) => ( ))}
{/* Sayfalar */}

Sayfalar

{SAYFALAR.map((s) => ( {s.ad} ))}
)}
); }