feat(kvkk): çerez politikası, ölçümü kapatma ve ilk ziyaret bilgi bandı
All checks were successful
Deploy / deploy (push) Successful in 6m27s

- /cerez-politikasi: çerez aydınlatma metni (ad, amaç, süre, taraf),
  Rybbit ölçümünün kapsamı ve hukuki dayanağı (KVKK md. 5/2-f,
  Çerez Rehberi Temmuz 2025 §5.9)
- OlcumTercihi: Rybbit'i tarayıcıda kapatan düğme (disable-rybbit)
- CerezBilgi: ilk ziyarette bir kez görünen bilgilendirme (onay
  penceresi değil; rıza gerektiren çerez yok)
- gizlilik: analitik bölümü kodla eşitlendi (e-posta/ad gönderilmiyor)
- alt bilgi ve sitemap'e sayfa eklendi
- docs: hukuki analiz ve meşru menfaat denge testi

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-10-05 10:11:38 +03:00
parent ff86176d9e
commit 9e4c5608eb
8 changed files with 629 additions and 13 deletions

View File

@@ -0,0 +1,80 @@
"use client";
// İlk ziyarette bir kez görünen çerez bilgilendirmesi. Onay penceresi DEĞİL:
// sitede açık rıza gerektiren çerez yok (zorunlu çerezler + kendi
// sunucumuzdaki ölçüm; bkz. /cerez-politikasi). KVKK Çerez Rehberi (Temmuz
// 2025) §9, veri işleme şartından bağımsız olarak siteye girişte
// bilgilendirme bekliyor; bu bant onu karşılar. Kabul/ret düğmesi yoktur,
// çünkü sorulan bir rıza yoktur — ölçümü kapatma anahtarı politika
// sayfasındadır. Kapatma kalıcıdır (localStorage).
import { useEffect, useState } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { Button } from "@/components/ui/button";
import { ViewportPortal } from "@/components/viewport-portal";
import { kromGizliMi } from "@/components/header-gate";
const GORULDU_KEY = "kolaytercih.cerez-bilgi";
export function CerezBilgi() {
const pathname = usePathname();
// SSR/ilk boyada gizli başla; durum mount'ta okunur.
const [goruldu, setGoruldu] = useState(true);
useEffect(() => {
try {
// eslint-disable-next-line react-hooks/set-state-in-effect
setGoruldu(Boolean(localStorage.getItem(GORULDU_KEY)));
} catch {
// Depolama kapalıysa "görüldü" saklanamaz; her sayfada çıkmasın.
setGoruldu(true);
}
}, []);
if (goruldu) return null;
// Giriş/ödeme gibi kromsuz akışlarda ve politikanın kendisinde gösterilmez.
if (pathname && kromGizliMi(pathname)) return null;
if (pathname === "/cerez-politikasi") return null;
function kapat() {
setGoruldu(true);
try {
localStorage.setItem(GORULDU_KEY, "1");
} catch {}
}
return (
<ViewportPortal>
{/* z-45: KaydetBanner'ın (z-40) üstünde, modalların (z-50) altında */}
<div className="pointer-events-none fixed inset-x-0 bottom-4 z-45 px-4 print:hidden">
<section
aria-label="Çerez bilgilendirmesi"
className="pointer-events-auto mx-auto flex max-w-xl flex-wrap items-center gap-3 rounded-2xl border border-slate-200 bg-card p-4 shadow-xl sm:flex-nowrap sm:gap-4"
>
<p className="min-w-0 flex-1 text-sm leading-snug text-slate-700">
Bu sitede reklam ya da izleme çerezi yok. Yalnız oturumun ve listen
için gerekli çerezleri ve kendi sunucumuzda çalışan çerezsiz
ziyaret ölçümünü kullanıyoruz.{" "}
<Link
href="/cerez-politikasi"
onClick={kapat}
className="font-semibold text-primary underline"
>
Ayrıntılar ve ölçümü kapatma
</Link>
</p>
<Button
type="button"
variant="outline"
size="lg"
onClick={kapat}
className="w-full cursor-pointer px-6 transition-[background-color,border-color,color,transform] duration-150 hover:border-orange-500 hover:bg-orange-50/60 hover:text-orange-500 active:scale-[0.97] sm:w-auto"
>
Tamam
</Button>
</section>
</div>
</ViewportPortal>
);
}