Files
config-vault/app/(dashboard)/dashboard/apps/new/page.tsx
T
2026-08-26 14:21:53 +03:00

203 lines
8.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
import Link from 'next/link'
import { ArrowLeft, Sparkles, Loader2, Rocket, Globe, Terminal, Smartphone } from 'lucide-react'
import { Navbar } from '@/components/Navbar'
import { IconPicker } from '@/components/IconPicker'
import { slugify } from '@/lib/utils'
export default function NewAppPage() {
const router = useRouter()
const [name, setName] = useState('')
const [slug, setSlug] = useState('')
const [description, setDescription] = useState('')
const [iconUrl, setIconUrl] = useState('')
const [slugEdited, setSlugEdited] = useState(false)
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
function handleNameChange(value: string) {
setName(value)
if (!slugEdited) {
setSlug(slugify(value))
}
}
async function handleSubmit(e: React.FormEvent) {
e.preventDefault()
setLoading(true)
setError('')
try {
const res = await fetch('/api/apps', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, slug, description, icon_url: iconUrl || null }),
})
const data = await res.json()
setLoading(false)
if (!res.ok) {
setError(data.error ?? 'Uygulama oluşturulurken bir hata oluştu.')
return
}
router.push(`/dashboard/apps/${data.id}`)
} catch {
setLoading(false)
setError('Ağ hatası oluştu, lütfen tekrar deneyin.')
}
}
return (
<div className="min-h-screen pb-16">
<Navbar />
<main className="max-w-2xl mx-auto px-4 sm:px-6 pt-8 animate-fade-in">
<Link
href="/dashboard"
className="inline-flex items-center gap-2 text-slate-400 hover:text-white text-xs font-medium mb-6 transition-colors px-3 py-1.5 rounded-lg hover:bg-slate-900 border border-transparent hover:border-slate-800"
>
<ArrowLeft className="w-3.5 h-3.5" />
<span>Uygulamalara Geri Dön</span>
</Link>
{/* Page Title */}
<div className="mb-6">
<h1 className="text-2xl font-bold text-white tracking-tight flex items-center gap-2.5">
<span>Yeni Uygulama Oluştur</span>
<div className="w-6 h-6 rounded-lg bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-emerald-400">
<Rocket className="w-3.5 h-3.5" />
</div>
</h1>
<p className="text-slate-400 text-sm mt-1">
Uygulamanız için dinamik config yönetimi ve otomatik 3 ortam (Development, Staging, Production) kurulacaktır.
</p>
</div>
{/* Creation Card */}
<div className="glass-panel rounded-3xl p-7 sm:p-8 shadow-2xl relative overflow-hidden">
<div className="absolute top-0 left-0 right-0 h-[1px] bg-gradient-to-r from-transparent via-emerald-500/50 to-transparent" />
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label className="block text-xs font-semibold text-slate-300 uppercase tracking-wider mb-2">
Uygulama Adı <span className="text-rose-400">*</span>
</label>
<input
type="text"
value={name}
onChange={e => handleNameChange(e.target.value)}
required
autoFocus
placeholder="Örn: Trendyol Satıcı Paneli, Mobil E-Ticaret"
className="w-full glass-input rounded-xl px-4 py-3 text-white placeholder-slate-500 focus:outline-none text-sm font-medium"
/>
</div>
<div>
<label className="block text-xs font-semibold text-slate-300 uppercase tracking-wider mb-2">
Benzersiz Tanımlayıcı (Slug) <span className="text-rose-400">*</span>
</label>
<div className="flex items-center glass-input rounded-xl overflow-hidden focus-within:ring-2 focus-within:ring-emerald-500/30 focus-within:border-emerald-500/60">
<span className="px-3.5 text-slate-500 text-xs font-mono border-r border-slate-800 bg-slate-900/50 py-3 select-none">
app/
</span>
<input
type="text"
value={slug}
onChange={e => {
setSlug(e.target.value)
setSlugEdited(true)
}}
required
placeholder="mobil-e-ticaret"
pattern="[a-z0-9-]+"
className="flex-1 bg-transparent px-3.5 py-3 text-white placeholder-slate-500 focus:outline-none text-sm font-mono"
/>
</div>
<p className="text-slate-500 text-[11px] mt-1.5">
Yalnızca küçük harfler, rakamlar ve tire (-) kullanılabilir. API isteklerinde kullanılacaktır.
</p>
</div>
<div>
<label className="block text-xs font-semibold text-slate-300 uppercase tracking-wider mb-2">
Açıklama <span className="text-slate-500 font-normal lowercase">(isteğe bağlı)</span>
</label>
<textarea
value={description}
onChange={e => setDescription(e.target.value)}
placeholder="Bu uygulama ne amaçla kullanılıyor ve hangi platformlarda çalışıyor?"
rows={3}
className="w-full glass-input rounded-xl px-4 py-3 text-white placeholder-slate-500 focus:outline-none text-sm font-medium resize-none leading-relaxed"
/>
</div>
{/* Icon Picker */}
<IconPicker
value={iconUrl}
onChange={setIconUrl}
appName={name}
/>
{/* Quick target platforms hint */}
<div className="p-4 rounded-2xl bg-slate-900/50 border border-slate-800/80 space-y-2">
<span className="text-[11px] font-semibold uppercase tracking-wider text-slate-400 flex items-center gap-1.5">
<Sparkles className="w-3.5 h-3.5 text-emerald-400" />
Uyumlu Platformlar & Entegrasyon
</span>
<div className="flex flex-wrap gap-2 text-xs text-slate-400 pt-1">
<span className="inline-flex items-center gap-1 px-2.5 py-1 rounded-lg bg-slate-800/80 border border-slate-700/50">
<Smartphone className="w-3.5 h-3.5 text-cyan-400" />
React Native / Expo
</span>
<span className="inline-flex items-center gap-1 px-2.5 py-1 rounded-lg bg-slate-800/80 border border-slate-700/50">
<Globe className="w-3.5 h-3.5 text-emerald-400" />
Next.js & React Web
</span>
<span className="inline-flex items-center gap-1 px-2.5 py-1 rounded-lg bg-slate-800/80 border border-slate-700/50">
<Terminal className="w-3.5 h-3.5 text-purple-400" />
Node.js / Express Backend
</span>
</div>
</div>
{error && (
<div className="bg-rose-500/10 border border-rose-500/30 rounded-xl p-3.5 text-rose-300 text-sm animate-slide-up">
{error}
</div>
)}
<div className="flex items-center gap-3 pt-2">
<Link
href="/dashboard"
className="flex-1 text-center bg-slate-800/80 hover:bg-slate-700/80 text-slate-300 text-sm font-semibold py-3 rounded-xl transition-colors border border-slate-700/60"
>
İptal
</Link>
<button
type="submit"
disabled={loading || !name || !slug}
className="flex-1 bg-emerald-500 hover:bg-emerald-400 active:bg-emerald-600 disabled:opacity-50 disabled:cursor-not-allowed text-slate-950 text-sm font-bold py-3 rounded-xl transition-all shadow-lg shadow-emerald-500/25 flex items-center justify-center gap-2"
>
{loading ? (
<>
<Loader2 className="w-4 h-4 animate-spin" />
<span>Oluşturuluyor...</span>
</>
) : (
<span>Uygulamayı Oluştur</span>
)}
</button>
</div>
</form>
</div>
</main>
</div>
)
}