feat: add bulk import and paste feature for .env and json configs

This commit is contained in:
2026-08-26 15:37:49 +03:00
parent fef66cd9dd
commit ac00ae1e78
3 changed files with 654 additions and 10 deletions
+56 -10
View File
@@ -7,12 +7,14 @@ import { App, AppEnvironment, AuditLog, ConfigEntry, ConfigType, Environment } f
import { cn, envBadgeStyles, maskSecret, typeColor } from '@/lib/utils'
import { AppIcon } from './AppIcon'
import { IconPicker } from './IconPicker'
import { BulkImportModal } from './BulkImportModal'
import {
Eye, EyeOff, Copy, Plus, Trash2, Edit2, Check, X,
Key, RefreshCw, Clock, Search, Download, FileCode,
Shield, Code2, AlertTriangle, Terminal, Smartphone,
Globe, Sparkles, Filter, ChevronRight, CheckCircle2,
Lock, ExternalLink, HelpCircle, Settings, Settings2, ImageIcon
Lock, ExternalLink, HelpCircle, Settings, Settings2, ImageIcon,
FileText, ClipboardPaste
} from 'lucide-react'
interface Props {
@@ -36,6 +38,7 @@ export function AppDetailClient({ app, environments, configs, auditLogs }: Props
const [copiedSlug, setCopiedSlug] = useState(false)
const [showAddModal, setShowAddModal] = useState(false)
const [showEditModal, setShowEditModal] = useState(false)
const [showBulkModal, setShowBulkModal] = useState(false)
const [revealedIds, setRevealedIds] = useState<Set<string>>(new Set())
const [codeSnippetTab, setCodeSnippetTab] = useState<'rn' | 'next' | 'curl'>('rn')
const [isDeletingApp, setIsDeletingApp] = useState(false)
@@ -144,7 +147,7 @@ export function AppDetailClient({ app, environments, configs, auditLogs }: Props
</div>
{/* Quick App Actions */}
<div className="flex items-center gap-2.5 self-start md:self-auto pt-2 md:pt-0">
<div className="flex flex-wrap items-center gap-2.5 self-start md:self-auto pt-2 md:pt-0">
<button
onClick={() => setShowEditModal(true)}
className="flex items-center gap-2 bg-slate-900 hover:bg-slate-800 border border-slate-800 hover:border-slate-700 text-slate-300 hover:text-white font-semibold text-xs sm:text-sm px-3.5 py-2.5 rounded-xl transition-all"
@@ -154,6 +157,15 @@ export function AppDetailClient({ app, environments, configs, auditLogs }: Props
<span>İkon / Ayarlar</span>
</button>
<button
onClick={() => setShowBulkModal(true)}
className="flex items-center gap-2 bg-slate-900 hover:bg-slate-800 border border-emerald-500/30 hover:border-emerald-500/50 text-emerald-400 hover:text-emerald-300 font-semibold text-xs sm:text-sm px-3.5 py-2.5 rounded-xl transition-all shadow-sm"
title=".env veya JSON içeriğini toplu yapıştır"
>
<ClipboardPaste className="w-4 h-4" />
<span>Toplu Yapıştır (.env)</span>
</button>
<button
onClick={() => setShowAddModal(true)}
className="flex items-center gap-2 bg-emerald-500 hover:bg-emerald-400 active:bg-emerald-600 text-slate-950 font-bold text-xs sm:text-sm px-4 py-2.5 rounded-xl shadow-lg shadow-emerald-500/20 transition-all hover:scale-[1.02]"
@@ -250,8 +262,17 @@ export function AppDetailClient({ app, environments, configs, auditLogs }: Props
})}
</div>
{/* Quick Export Tools */}
{/* Quick Import & Export Tools */}
<div className="flex flex-wrap items-center gap-2">
<button
onClick={() => setShowBulkModal(true)}
className="inline-flex items-center gap-1.5 px-3 py-2 rounded-xl bg-emerald-500/10 border border-emerald-500/30 hover:bg-emerald-500/20 text-xs font-semibold text-emerald-400 hover:text-emerald-300 transition-colors shadow-sm"
title=".env veya JSON içeriğini toplu içe aktar"
>
<ClipboardPaste className="w-3.5 h-3.5" />
<span>Toplu Yapıştır</span>
</button>
<button
onClick={exportAsEnv}
className="inline-flex items-center gap-1.5 px-3 py-2 rounded-xl bg-slate-900 border border-slate-800 hover:border-slate-700 text-xs font-medium text-slate-300 hover:text-white transition-colors"
@@ -338,13 +359,23 @@ export function AppDetailClient({ app, environments, configs, auditLogs }: Props
: 'Uygulamanızın ihtiyaç duyduğu API URL, Secret veya Flag değişkenlerini ekleyin.'}
</p>
{!searchQuery && typeFilter === 'all' && (
<button
onClick={() => setShowAddModal(true)}
className="inline-flex items-center gap-1.5 bg-emerald-500 hover:bg-emerald-400 text-slate-950 text-xs font-bold px-4 py-2 rounded-xl transition-transform hover:scale-105 shadow-md shadow-emerald-500/20"
>
<Plus className="w-3.5 h-3.5 stroke-[2.5]" />
<span>İlk Değişkeni Ekle</span>
</button>
<div className="flex flex-wrap items-center justify-center gap-3">
<button
onClick={() => setShowAddModal(true)}
className="inline-flex items-center gap-1.5 bg-emerald-500 hover:bg-emerald-400 text-slate-950 text-xs font-bold px-4 py-2.5 rounded-xl transition-transform hover:scale-105 shadow-md shadow-emerald-500/20"
>
<Plus className="w-3.5 h-3.5 stroke-[2.5]" />
<span>Tek Tek Ekle</span>
</button>
<button
onClick={() => setShowBulkModal(true)}
className="inline-flex items-center gap-1.5 bg-slate-900 hover:bg-slate-800 border border-emerald-500/30 hover:border-emerald-500/50 text-emerald-400 hover:text-emerald-300 text-xs font-semibold px-4 py-2.5 rounded-xl transition-colors"
>
<ClipboardPaste className="w-3.5 h-3.5" />
<span>Toplu Yapıştır (.env / JSON)</span>
</button>
</div>
)}
</div>
)}
@@ -656,6 +687,21 @@ export async function getAppConfig(env = 'production') {
}}
/>
)}
{/* ─── BULK IMPORT / PASTE MODAL ─── */}
{showBulkModal && (
<BulkImportModal
environmentId={currentEnv?.id ?? ''}
environment={activeEnv}
appId={app.id}
existingKeys={envConfigs.map(c => c.key)}
onClose={() => setShowBulkModal(false)}
onSaved={() => {
setShowBulkModal(false)
router.refresh()
}}
/>
)}
</div>
)
}