main
UI Deploy (Next-Auth Support) 🎨 / build-and-deploy (push) Has been cancelled

This commit is contained in:
Harun CAN
2026-03-29 12:44:02 +03:00
parent fe9aff3fec
commit 45a540c530
26 changed files with 10706 additions and 86 deletions
@@ -0,0 +1,272 @@
"use client";
import { useState } from "react";
import { motion } from "framer-motion";
import {
Search,
Star,
Copy,
Eye,
TrendingUp,
Clock,
Sparkles,
Filter,
} from "lucide-react";
import { cn } from "@/lib/utils";
interface Template {
id: string;
title: string;
description: string;
category: string;
language: string;
usageCount: number;
rating: number;
duration: number;
style: string;
featured: boolean;
}
const mockTemplates: Template[] = [
{
id: "t1",
title: "Evrenin Gizemli Boşlukları",
description: "Uzaydaki devasa boşlukları ve karanlık maddeyi keşfet",
category: "Bilim",
language: "tr",
usageCount: 342,
rating: 4.8,
duration: 45,
style: "CINEMATIC",
featured: true,
},
{
id: "t2",
title: "5 Mind-Blowing Physics Facts",
description: "Quantum mechanics to relativity in 60 seconds",
category: "Education",
language: "en",
usageCount: 1205,
rating: 4.9,
duration: 60,
style: "EDUCATIONAL",
featured: true,
},
{
id: "t3",
title: "Mitolojik Yaratıklar",
description: "Antik medeniyetlerin efsanevi canlıları",
category: "Tarih",
language: "tr",
usageCount: 189,
rating: 4.6,
duration: 50,
style: "STORYTELLING",
featured: false,
},
{
id: "t4",
title: "Secretos del Océano Profundo",
description: "Criaturas bioluminiscentes y volcanes submarinos",
category: "Ciencia",
language: "es",
usageCount: 567,
rating: 4.7,
duration: 55,
style: "DOCUMENTARY",
featured: false,
},
{
id: "t5",
title: "AI Tüm Meslekleri Yok Edecek mi?",
description: "Yapay zekanın iş dünyasına etkisi ve gelecek senaryoları",
category: "Teknoloji",
language: "tr",
usageCount: 891,
rating: 4.5,
duration: 60,
style: "NEWS",
featured: true,
},
{
id: "t6",
title: "Die Geheimnisse der Pyramiden",
description: "Ägyptische Pyramiden und ihre versteckten Kammern",
category: "Geschichte",
language: "de",
usageCount: 234,
rating: 4.4,
duration: 40,
style: "DOCUMENTARY",
featured: false,
},
];
const categories = ["Tümü", "Bilim", "Education", "Tarih", "Teknoloji", "Ciencia", "Geschichte"];
const sortOptions = [
{ id: "popular", label: "En Popüler", icon: TrendingUp },
{ id: "newest", label: "En Yeni", icon: Clock },
{ id: "rating", label: "En İyi Puan", icon: Star },
];
const flagEmoji: Record<string, string> = {
tr: "🇹🇷",
en: "🇺🇸",
es: "🇪🇸",
de: "🇩🇪",
fr: "🇫🇷",
};
const stagger = {
hidden: { opacity: 0 },
show: { opacity: 1, transition: { staggerChildren: 0.06 } },
};
const fadeUp = {
hidden: { opacity: 0, y: 16, scale: 0.97 },
show: { opacity: 1, y: 0, scale: 1, transition: { duration: 0.5, ease: [0.16, 1, 0.3, 1] as const } },
};
export default function TemplatesPage() {
const [search, setSearch] = useState("");
const [activeCategory, setActiveCategory] = useState("Tümü");
const [activeSort, setActiveSort] = useState("popular");
const filtered = mockTemplates.filter((t) => {
if (activeCategory !== "Tümü" && t.category !== activeCategory) return false;
if (search && !t.title.toLowerCase().includes(search.toLowerCase())) return false;
return true;
});
return (
<div className="max-w-6xl mx-auto space-y-6">
{/* ── Başlık ── */}
<div>
<h1 className="font-[family-name:var(--font-display)] text-2xl md:text-3xl font-bold">
<Sparkles size={24} className="inline mr-2 text-violet-400" />
Şablon Galerisi
</h1>
<p className="text-sm text-[var(--color-text-muted)] mt-1">
Topluluk şablonlarını keşfet, tek tıkla kendi projene klonla
</p>
</div>
{/* ── Arama + Filtreler ── */}
<div className="flex flex-col sm:flex-row gap-3">
<div className="relative flex-1">
<Search size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-[var(--color-text-ghost)]" />
<input
type="text"
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Şablon ara..."
className="w-full pl-9 pr-4 py-2.5 rounded-xl bg-[var(--color-bg-surface)] border border-[var(--color-border-faint)] text-sm text-[var(--color-text-primary)] placeholder:text-[var(--color-text-ghost)] focus:outline-none focus:border-violet-500/40 transition-colors"
/>
</div>
<div className="flex gap-1.5">
{sortOptions.map((opt) => {
const Icon = opt.icon;
return (
<button
key={opt.id}
onClick={() => setActiveSort(opt.id)}
className={cn(
"flex items-center gap-1.5 px-3 py-2 rounded-xl text-xs font-medium transition-all",
activeSort === opt.id
? "bg-violet-500/12 text-violet-400 border border-violet-500/25"
: "text-[var(--color-text-muted)] border border-[var(--color-border-faint)] hover:border-[var(--color-border-default)]"
)}
>
<Icon size={13} />
<span className="hidden sm:inline">{opt.label}</span>
</button>
);
})}
</div>
</div>
{/* Kategori Tabs */}
<div className="flex items-center gap-1.5 overflow-x-auto pb-1 scrollbar-none">
{categories.map((cat) => (
<button
key={cat}
onClick={() => setActiveCategory(cat)}
className={cn(
"px-3.5 py-1.5 rounded-full text-xs font-medium whitespace-nowrap transition-all",
activeCategory === cat
? "bg-violet-500/15 text-violet-400 border border-violet-500/25"
: "text-[var(--color-text-muted)] border border-[var(--color-border-faint)] hover:border-[var(--color-border-default)]"
)}
>
{cat}
</button>
))}
</div>
{/* ── Grid ── */}
<motion.div
variants={stagger}
initial="hidden"
animate="show"
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"
>
{filtered.map((template) => (
<motion.div key={template.id} variants={fadeUp}>
<div className="group card-surface overflow-hidden hover:border-violet-500/20">
{/* Cover */}
<div className="relative h-36 bg-gradient-to-br from-[var(--color-bg-elevated)] to-[var(--color-bg-surface)] flex items-center justify-center overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-violet-500/5 to-cyan-500/5" />
<span className="text-4xl opacity-70">{flagEmoji[template.language] || "🌍"}</span>
{template.featured && (
<span className="absolute top-3 left-3 badge badge-violet text-[9px]">
Öne Çıkan
</span>
)}
<div className="absolute top-3 right-3 flex items-center gap-1 badge badge-amber text-[10px]">
<Star size={10} className="fill-amber-400" />
{template.rating}
</div>
{/* Hover overlay */}
<div className="absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center gap-3">
<button className="w-10 h-10 rounded-xl bg-white/10 backdrop-blur flex items-center justify-center text-white hover:bg-white/20 transition-colors">
<Eye size={18} />
</button>
<button className="w-10 h-10 rounded-xl bg-violet-500/80 backdrop-blur flex items-center justify-center text-white hover:bg-violet-500 transition-colors">
<Copy size={18} />
</button>
</div>
</div>
{/* Info */}
<div className="p-4">
<h3 className="text-sm font-semibold line-clamp-1 group-hover:text-violet-300 transition-colors">
{template.title}
</h3>
<p className="text-xs text-[var(--color-text-muted)] mt-1 line-clamp-2">
{template.description}
</p>
<div className="flex items-center justify-between mt-3">
<div className="flex items-center gap-2 text-[10px] text-[var(--color-text-ghost)]">
<span>{template.duration}s</span>
<span></span>
<span>{template.usageCount} kullanım</span>
</div>
<button className="flex items-center gap-1 text-[11px] font-medium text-violet-400 hover:text-violet-300 transition-colors">
<Copy size={12} />
Klonla
</button>
</div>
</div>
</div>
</motion.div>
))}
</motion.div>
{filtered.length === 0 && (
<div className="text-center py-16">
<p className="text-[var(--color-text-muted)]">Aramanızla eşleşen şablon bulunamadı</p>
</div>
)}
</div>
);
}