first commit

This commit is contained in:
2026-08-26 14:21:53 +03:00
commit fef66cd9dd
38 changed files with 5783 additions and 0 deletions
+239
View File
@@ -0,0 +1,239 @@
'use client'
import { useState } from 'react'
import Link from 'next/link'
import { Plus, Search, Layers, Key, ArrowUpRight, Clock, Sparkles, SlidersHorizontal, ShieldCheck, Zap } from 'lucide-react'
import { App } from '@/types'
import { AppIcon } from './AppIcon'
interface DashboardApp extends App {
config_count?: number | string
}
interface Props {
initialApps: DashboardApp[]
totalConfigsCount: number
totalAuditCount: number
}
export function DashboardClient({ initialApps, totalConfigsCount, totalAuditCount }: Props) {
const [search, setSearch] = useState('')
const filteredApps = initialApps.filter(app =>
app.name.toLowerCase().includes(search.toLowerCase()) ||
app.slug.toLowerCase().includes(search.toLowerCase()) ||
(app.description && app.description.toLowerCase().includes(search.toLowerCase()))
)
return (
<div className="space-y-8 animate-fade-in">
{/* Hero / Stat cards */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{/* Stat 1 */}
<div className="glass-panel p-5 rounded-2xl relative overflow-hidden group">
<div className="absolute top-0 right-0 w-24 h-24 bg-emerald-500/10 rounded-full blur-2xl group-hover:bg-emerald-500/20 transition-colors" />
<div className="flex items-center justify-between mb-3">
<span className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Kayıtlı Uygulamalar</span>
<div className="w-8 h-8 rounded-xl bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-emerald-400">
<Layers className="w-4 h-4" />
</div>
</div>
<div className="flex items-baseline gap-2">
<span className="text-3xl font-extrabold text-white tracking-tight">{initialApps.length}</span>
<span className="text-xs text-emerald-400 font-medium">Aktif Servis</span>
</div>
</div>
{/* Stat 2 */}
<div className="glass-panel p-5 rounded-2xl relative overflow-hidden group">
<div className="absolute top-0 right-0 w-24 h-24 bg-cyan-500/10 rounded-full blur-2xl group-hover:bg-cyan-500/20 transition-colors" />
<div className="flex items-center justify-between mb-3">
<span className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Toplam Config & Secret</span>
<div className="w-8 h-8 rounded-xl bg-cyan-500/10 border border-cyan-500/20 flex items-center justify-center text-cyan-400">
<Key className="w-4 h-4" />
</div>
</div>
<div className="flex items-baseline gap-2">
<span className="text-3xl font-extrabold text-white tracking-tight">{totalConfigsCount}</span>
<span className="text-xs text-cyan-400 font-medium">Değer Tanımlı</span>
</div>
</div>
{/* Stat 3 */}
<div className="glass-panel p-5 rounded-2xl relative overflow-hidden group">
<div className="absolute top-0 right-0 w-24 h-24 bg-purple-500/10 rounded-full blur-2xl group-hover:bg-purple-500/20 transition-colors" />
<div className="flex items-center justify-between mb-3">
<span className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Ortam Desteği</span>
<div className="w-8 h-8 rounded-xl bg-purple-500/10 border border-purple-500/20 flex items-center justify-center text-purple-400">
<Zap className="w-4 h-4" />
</div>
</div>
<div className="flex items-baseline gap-2">
<span className="text-3xl font-extrabold text-white tracking-tight">3</span>
<span className="text-xs text-purple-400 font-medium">Dev · Stage · Prod</span>
</div>
</div>
{/* Stat 4 */}
<div className="glass-panel p-5 rounded-2xl relative overflow-hidden group">
<div className="absolute top-0 right-0 w-24 h-24 bg-amber-500/10 rounded-full blur-2xl group-hover:bg-amber-500/20 transition-colors" />
<div className="flex items-center justify-between mb-3">
<span className="text-xs font-semibold text-slate-400 uppercase tracking-wider">İşlem Denetimi</span>
<div className="w-8 h-8 rounded-xl bg-amber-500/10 border border-amber-500/20 flex items-center justify-center text-amber-400">
<ShieldCheck className="w-4 h-4" />
</div>
</div>
<div className="flex items-baseline gap-2">
<span className="text-3xl font-extrabold text-white tracking-tight">{totalAuditCount}</span>
<span className="text-xs text-amber-400 font-medium">Audit Kaydı</span>
</div>
</div>
</div>
{/* Action Header & Search */}
<div className="flex flex-col sm:flex-row items-stretch sm:items-center justify-between gap-4 pt-2">
<div className="relative flex-1 max-w-md">
<Search className="w-4 h-4 text-slate-400 absolute left-3.5 top-1/2 -translate-y-1/2" />
<input
type="text"
value={search}
onChange={e => setSearch(e.target.value)}
placeholder="Uygulama adı veya slug ara..."
className="w-full glass-input rounded-xl pl-10 pr-4 py-2.5 text-sm text-white placeholder-slate-500 focus:outline-none"
/>
{search && (
<button
onClick={() => setSearch('')}
className="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-slate-400 hover:text-white"
>
Temizle
</button>
)}
</div>
<Link
href="/dashboard/apps/new"
className="inline-flex items-center justify-center gap-2 bg-emerald-500 hover:bg-emerald-400 active:bg-emerald-600 text-slate-950 font-bold text-sm px-5 py-2.5 rounded-xl shadow-lg shadow-emerald-500/20 transition-all hover:scale-[1.02]"
>
<Plus className="w-4 h-4 stroke-[2.5]" />
<span>Yeni Uygulama Ekle</span>
</Link>
</div>
{/* App Cards Grid */}
{filteredApps.length === 0 ? (
<div className="glass-panel text-center py-16 px-4 rounded-3xl border border-dashed border-slate-800">
<div className="w-14 h-14 bg-slate-900 border border-slate-800 rounded-2xl flex items-center justify-center mx-auto mb-4 text-slate-500">
<SlidersHorizontal className="w-7 h-7" />
</div>
<h3 className="text-white text-lg font-bold mb-1">
{search ? 'Eşleşen uygulama bulunamadı' : 'Henüz hiç uygulama eklenmemiş'}
</h3>
<p className="text-slate-400 text-sm max-w-md mx-auto mb-6">
{search
? `"${search}" aramasıyla eşleşen bir sonuç yok. Başka bir anahtar kelime deneyin.`
: 'ConfigVault ile mobil ve web uygulamalarınızın ortam değişkenlerini dinamik olarak yönetmeye başlamak için ilk uygulamanızı ekleyin.'}
</p>
{!search && (
<Link
href="/dashboard/apps/new"
className="inline-flex items-center gap-2 bg-emerald-500 hover:bg-emerald-400 text-slate-950 font-bold text-sm px-5 py-2.5 rounded-xl shadow-lg shadow-emerald-500/25 transition-transform hover:scale-105"
>
<Plus className="w-4 h-4 stroke-[2.5]" />
<span>İlk Uygulamayı Oluştur</span>
</Link>
)}
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
{filteredApps.map(app => (
<AppCard key={app.id} app={app} />
))}
</div>
)}
</div>
)
}
function AppCard({ app }: { app: DashboardApp }) {
const formattedDate = new Date(app.updated_at).toLocaleDateString('tr-TR', {
day: 'numeric',
month: 'short',
year: 'numeric',
})
// Color theme generator based on app name initial
const initial = app.name.charAt(0).toUpperCase()
const gradients = [
'from-emerald-500 to-teal-700',
'from-cyan-500 to-blue-700',
'from-indigo-500 to-purple-700',
'from-amber-500 to-orange-700',
'from-rose-500 to-pink-700',
]
const gradientIndex = (initial.charCodeAt(0) || 0) % gradients.length
const bgGradient = gradients[gradientIndex]
return (
<Link
href={`/dashboard/apps/${app.id}`}
className="glass-panel-interactive rounded-2xl p-6 flex flex-col justify-between group relative overflow-hidden"
>
{/* Ambient background hover flare */}
<div className="absolute top-0 right-0 w-32 h-32 bg-emerald-500/5 group-hover:bg-emerald-500/10 rounded-full blur-2xl transition-all duration-300 pointer-events-none" />
<div>
{/* Top bar: Avatar & Arrow */}
<div className="flex items-start justify-between mb-4">
<AppIcon name={app.name} iconUrl={app.icon_url} size="lg" />
<div className="w-8 h-8 rounded-xl bg-slate-800/60 border border-slate-700/50 flex items-center justify-center text-slate-400 group-hover:text-emerald-400 group-hover:border-emerald-500/30 group-hover:bg-emerald-500/10 transition-all">
<ArrowUpRight className="w-4 h-4 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5" />
</div>
</div>
{/* Title & Slug */}
<h3 className="text-white text-lg font-bold group-hover:text-emerald-300 transition-colors tracking-tight">
{app.name}
</h3>
<div className="inline-flex items-center gap-1 mt-1 px-2 py-0.5 rounded-md bg-slate-900/90 border border-slate-800 text-[11px] font-mono text-slate-400">
<span>slug:</span>
<span className="text-emerald-400 font-semibold">{app.slug}</span>
</div>
{/* Description */}
<p className="text-slate-400 text-xs mt-3 line-clamp-2 leading-relaxed min-h-[32px]">
{app.description || <span className="text-slate-600 italic">Açıklama girilmemiş</span>}
</p>
</div>
{/* Bottom meta details */}
<div className="mt-6 pt-4 border-t border-slate-800/80 space-y-3">
<div className="flex items-center justify-between text-xs">
<div className="flex items-center gap-1.5 text-slate-400">
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400" />
<span className="font-mono text-[11px]">
{Number(app.config_count) || 0} config kaydı
</span>
</div>
<div className="flex items-center gap-1 text-[11px] text-slate-500">
<Clock className="w-3 h-3" />
<span>{formattedDate}</span>
</div>
</div>
{/* Env tag pills */}
<div className="flex items-center gap-1.5">
<span className="text-[10px] font-medium px-2 py-0.5 rounded bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">
prod
</span>
<span className="text-[10px] font-medium px-2 py-0.5 rounded bg-amber-500/10 text-amber-400 border border-amber-500/20">
stage
</span>
<span className="text-[10px] font-medium px-2 py-0.5 rounded bg-cyan-500/10 text-cyan-400 border border-cyan-500/20">
dev
</span>
</div>
</div>
</Link>
)
}