feat: add privacy hub and application privacy subpages

This commit is contained in:
mstfyldz
2026-08-17 10:29:12 +03:00
parent 59b2afbc5a
commit 39b19bdee9
11 changed files with 1536 additions and 2 deletions
+344
View File
@@ -0,0 +1,344 @@
"use client";
import { motion, useScroll, useSpring } from "framer-motion";
import Link from "next/link";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import type { Locale } from "@/i18n-config";
import type { PrivacyApp } from "@/data/privacy";
const easeOutExpo = [0.16, 1, 0.3, 1] as [number, number, number, number];
export default function PrivacyDetailClient({
lang,
dict,
app,
otherApps,
}: {
lang: Locale;
dict: any;
app: PrivacyApp;
otherApps: PrivacyApp[];
}) {
const { scrollYProgress } = useScroll();
const scaleX = useSpring(scrollYProgress, {
stiffness: 100,
damping: 30,
restDelta: 0.001,
});
const isTr = lang === "tr";
return (
<div className="bg-[#F4F0E8] text-[#0A0A0A] font-body min-h-screen">
{/* Scroll indicator */}
<motion.div
className="fixed top-0 left-0 right-0 h-1.5 bg-[#FFE600] z-50 origin-[0%]"
style={{ scaleX }}
/>
<Header lang={lang} dict={dict.nav} />
<main className="pt-32 pb-24 px-6 max-w-6xl mx-auto">
{/* Back Link */}
<motion.div
className="mb-8"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
>
<Link
href={`/${lang}/privacy`}
className="inline-flex items-center gap-2 font-mono text-[10px] tracking-[0.25em] uppercase text-[#A0998E] hover:text-[#0A0A0A] transition-colors"
>
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<line x1="19" y1="12" x2="5" y2="12" />
<polyline points="12 19 5 12 12 5" />
</svg>
{isTr ? "TÜM GİZLİLİK POLİTİKALARI" : "ALL PRIVACY POLICIES"}
</Link>
</motion.div>
{/* Hero Header */}
<div className="border border-[#C8C2B8] bg-[#EDE8E0] p-8 sm:p-12 mb-12">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-6 mb-6">
<div className="flex items-center gap-4">
<div className="w-16 h-16 bg-[#FFE600] border-2 border-[#0A0A0A] flex items-center justify-center text-3xl flex-shrink-0">
{app.icon}
</div>
<div>
<div className="flex items-center gap-2 mb-1">
<span className="font-mono text-[9px] tracking-[0.2em] uppercase px-2 py-0.5 border border-[#C8C2B8] bg-[#F4F0E8] text-[#6A6460]">
{app.category[lang]}
</span>
<span className="font-mono text-[9px] text-[#A0998E]">
{app.version}
</span>
</div>
<h1 className="font-display font-black text-3xl sm:text-5xl uppercase text-[#0A0A0A] tracking-tight">
{app.name}
</h1>
</div>
</div>
<div className="flex flex-col md:items-end gap-2">
<div className="flex gap-1.5">
{app.platforms.map((p) => (
<span
key={p}
className="font-mono text-[9px] uppercase px-2.5 py-1 bg-[#0A0A0A] text-[#F4F0E8] font-bold"
>
{p}
</span>
))}
</div>
<div className="font-mono text-[10px] text-[#A0998E]">
{isTr ? "Yürürlük / Güncelleme:" : "Effective / Last Updated:"} {app.lastUpdated[lang]}
</div>
</div>
</div>
<p className="text-[#6A6460] text-base leading-relaxed max-w-4xl border-t border-[#C8C2B8] pt-6">
{app.summary[lang]}
</p>
{/* Key Highlights */}
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3 mt-6 pt-6 border-t border-[#C8C2B8]">
{app.highlights[lang].map((h, i) => (
<div key={i} className="bg-[#F4F0E8] p-3 border border-[#E0DACF]">
<div className="font-mono text-[8px] uppercase tracking-wider text-[#A0998E] mb-1">
{h.label}
</div>
<div className="font-display font-bold text-xs text-[#0A0A0A]">
{h.value}
</div>
</div>
))}
</div>
</div>
{/* Content Layout: Sticky Sidebar + Main Sections */}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12">
{/* Sidebar Navigation */}
<aside className="lg:col-span-4">
<div className="sticky top-24 border border-[#C8C2B8] bg-[#EDE8E0] p-6 space-y-6">
<div>
<div className="font-mono text-[9px] tracking-[0.25em] uppercase text-[#A0998E] mb-4">
{isTr ? "İÇİNDEKİLER" : "TABLE OF CONTENTS"}
</div>
<nav className="space-y-2">
{app.permissions.length > 0 && (
<a
href="#permissions"
className="block font-mono text-[11px] text-[#6A6460] hover:text-[#0A0A0A] hover:translate-x-1 transition-all py-1 border-b border-[#E0DACF]"
>
{isTr ? "→ Donanım & Sistem İzinleri" : "→ Hardware & Permissions"}
</a>
)}
{app.sections.map((sec) => (
<a
key={sec.id}
href={`#${sec.id}`}
className="block font-mono text-[11px] text-[#6A6460] hover:text-[#0A0A0A] hover:translate-x-1 transition-all py-1 border-b border-[#E0DACF]"
>
{sec.title[lang]}
</a>
))}
<a
href="#data-deletion"
className="block font-mono text-[11px] text-[#FF4500] font-bold hover:translate-x-1 transition-all py-1"
>
{isTr ? "Veri Silme & Destek" : "Data Erasure & Contact"}
</a>
</nav>
</div>
<div className="pt-4 border-t border-[#C8C2B8]">
<div className="font-mono text-[9px] uppercase tracking-wider text-[#A0998E] mb-2">
{isTr ? "Gizlilik İletişim Hattı:" : "Privacy Contact Desk:"}
</div>
<a
href={`mailto:${app.contactEmail}`}
className="font-mono text-xs text-[#0A0A0A] font-bold hover:underline"
>
{app.contactEmail}
</a>
</div>
</div>
</aside>
{/* Main Legal Content */}
<div className="lg:col-span-8 space-y-12">
{/* System Permissions (if applicable) */}
{app.permissions.length > 0 && (
<section id="permissions" className="border border-[#C8C2B8] bg-[#EDE8E0] p-8">
<div className="flex items-center gap-3 mb-6">
<span className="w-2.5 h-2.5 bg-[#FFE600]" />
<h2 className="font-display font-black text-xl uppercase text-[#0A0A0A]">
{isTr ? "Cihaz İzinleri ve Donanım Erişimi" : "Device Permissions & Hardware Access"}
</h2>
</div>
<p className="text-[#6A6460] text-sm leading-relaxed mb-6">
{isTr
? `${app.name} uygulaması, yalnızca temel işlevlerini kusursuz yerine getirebilmek için işletim sisteminizden (iOS / Android) aşağıdaki açık izinleri talep eder:`
: `${app.name} requests explicit permissions from your operating system (iOS / Android) solely to perform core functionality:`}
</p>
<div className="space-y-4">
{app.permissions.map((perm, idx) => (
<div
key={idx}
className="bg-[#F4F0E8] p-4 border border-[#C8C2B8] flex flex-col gap-2"
>
<div className="flex items-center justify-between">
<span className="font-display font-black text-sm uppercase text-[#0A0A0A]">
{perm.name[lang]}
</span>
<span
className={`font-mono text-[9px] uppercase px-2 py-0.5 font-bold ${
perm.required
? "bg-[#FFE600] text-[#0A0A0A]"
: "bg-[#E0DACF] text-[#6A6460]"
}`}
>
{perm.required
? isTr
? "ZORUNLU"
: "REQUIRED"
: isTr
? "İSTEĞE BAĞLI"
: "OPTIONAL"}
</span>
</div>
<p className="text-[#6A6460] text-xs leading-relaxed">
{perm.reason[lang]}
</p>
</div>
))}
</div>
</section>
)}
{/* Sections */}
{app.sections.map((section, idx) => (
<section
key={section.id}
id={section.id}
className="border-b border-[#C8C2B8] pb-10 scroll-mt-28"
>
<div className="flex items-center gap-3 mb-4">
<span className="font-mono text-[10px] text-[#A0998E]">
#{String(idx + 1).padStart(2, "0")}
</span>
<h2 className="font-display font-black text-xl sm:text-2xl uppercase text-[#0A0A0A]">
{section.title[lang]}
</h2>
</div>
<p className="text-[#4A4540] text-sm sm:text-base leading-relaxed mb-4">
{section.content[lang]}
</p>
{section.bullets && (
<ul className="space-y-2.5 mt-4 bg-[#EDE8E0] p-6 border-l-2 border-[#FFE600] border-y border-r border-[#C8C2B8]">
{section.bullets[lang].map((bullet, bIdx) => (
<li key={bIdx} className="flex items-start gap-2.5 text-xs sm:text-sm text-[#4A4540]">
<span className="font-mono text-[#A0998E] flex-shrink-0 mt-0.5"></span>
<span>{bullet}</span>
</li>
))}
</ul>
)}
</section>
))}
{/* Data Deletion and Erasure Request Section */}
<section
id="data-deletion"
className="border-2 border-[#0A0A0A] bg-[#FFE600] p-8"
>
<div className="font-mono text-[9px] uppercase tracking-[0.2em] text-[#0A0A0A] font-bold mb-2">
{isTr ? "KULLANICI VERİ KONTROLÜ" : "USER DATA CONTROL"}
</div>
<h2 className="font-display font-black text-2xl uppercase text-[#0A0A0A] mb-3">
{isTr
? `${app.name} Verilerinizi Kalıcı Olarak Silin`
: `Permanently Erase Your ${app.name} Data`}
</h2>
<p className="text-[#0A0A0A]/85 text-xs sm:text-sm leading-relaxed mb-6">
{isTr
? "Uygulama içerisinden hesabınızı silebilir veya aşağıdaki buton aracılığıyla doğrudan veri silme ve KVKK hak talebinde bulunabilirsiniz. Talebiniz en geç 72 saat içinde işleme alınır."
: "You can delete your account within the app or trigger a formal GDPR/KVKK erasure request by emailing our automated privacy handler. All records are purged within 72 hours."}
</p>
<a
href={`mailto:${app.contactEmail}?subject=${encodeURIComponent(
`[${app.name}] Veri Silme ve Hesap Kapatma Talebi`
)}&body=${encodeURIComponent(
`Merhaba,\n\n${app.name} uygulamasında kayıtlı olan kullanıcı hesabımın ve tüm verilerimin kalıcı olarak silinmesini talep ediyorum.\n\nKayıtlı E-posta Adresim:\nKullanıcı / Hesap ID (varsa):\n\nTeşekkürler.`
)}`}
className="inline-flex items-center gap-3 px-6 py-3.5 bg-[#0A0A0A] text-[#F4F0E8] font-display font-black text-xs tracking-wider uppercase hover:bg-[#FF4500] transition-colors"
>
{isTr ? "VERİ SİLME TALEBİ OLUŞTUR" : "REQUEST DATA DELETION"}
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<line x1="22" y1="2" x2="11" y2="13" />
<polygon points="22 2 15 22 11 13 2 9 22 2" />
</svg>
</a>
</section>
</div>
</div>
{/* Other Applications Footer Switcher */}
{otherApps.length > 0 && (
<div className="mt-24 pt-12 border-t border-[#C8C2B8]">
<div className="font-mono text-[10px] tracking-[0.25em] uppercase text-[#A0998E] mb-6">
{isTr ? "DİĞER UYGULAMA GİZLİLİK POLİTİKALARI" : "OTHER APPLICATION POLICIES"}
</div>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-6">
{otherApps.map((other) => (
<Link
key={other.slug}
href={`/${lang}/privacy/${other.slug}`}
className="border border-[#C8C2B8] bg-[#EDE8E0] p-5 hover:border-[#0A0A0A] transition-colors group flex items-center gap-4"
>
<div className="w-10 h-10 bg-[#FFE600] border border-[#0A0A0A] flex items-center justify-center text-xl flex-shrink-0">
{other.icon}
</div>
<div className="min-w-0">
<div className="font-display font-black text-sm uppercase text-[#0A0A0A] group-hover:text-[#FF4500] transition-colors truncate">
{other.name}
</div>
<div className="font-mono text-[9px] text-[#A0998E] truncate">
{other.category[lang]}
</div>
</div>
</Link>
))}
</div>
</div>
)}
</main>
<Footer lang={lang} dict={dict} />
</div>
);
}