import { useState, useEffect } from 'react' import { api } from '../utils/api' import UnifiedPromptModal from '../components/UnifiedPromptModal' import { Star, Trash2, Edit, Copy, Filter, ArrowDownToLine } from 'lucide-react' /** * Admin Prompts Page - Unified System (Issue #28 Phase 3) * * Manages both base and pipeline-type prompts in one interface. */ export default function AdminPromptsPage() { const [prompts, setPrompts] = useState([]) const [filteredPrompts, setFilteredPrompts] = useState([]) const [typeFilter, setTypeFilter] = useState('all') // 'all' | 'base' | 'pipeline' const [category, setCategory] = useState('all') const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [editingPrompt, setEditingPrompt] = useState(null) const [showNewPrompt, setShowNewPrompt] = useState(false) const [importing, setImporting] = useState(false) const [importResult, setImportResult] = useState(null) const categories = [ { id: 'all', label: 'Alle Kategorien' }, { id: 'körper', label: 'Körper' }, { id: 'ernährung', label: 'Ernährung' }, { id: 'training', label: 'Training' }, { id: 'schlaf', label: 'Schlaf' }, { id: 'vitalwerte', label: 'Vitalwerte' }, { id: 'ziele', label: 'Ziele' }, { id: 'ganzheitlich', label: 'Ganzheitlich' }, { id: 'pipeline', label: 'Pipeline' } ] useEffect(() => { loadPrompts() }, []) useEffect(() => { let filtered = prompts // Filter by type if (typeFilter === 'base') { filtered = filtered.filter(p => p.type === 'base') } else if (typeFilter === 'pipeline') { filtered = filtered.filter(p => p.type === 'pipeline') } // Filter by category if (category !== 'all') { filtered = filtered.filter(p => p.category === category) } setFilteredPrompts(filtered) }, [typeFilter, category, prompts]) const loadPrompts = async () => { try { setLoading(true) const data = await api.listAdminPrompts() setPrompts(data) setError(null) } catch (e) { setError(e.message) } finally { setLoading(false) } } const handleToggleActive = async (prompt) => { try { await api.updateUnifiedPrompt(prompt.id, { active: !prompt.active }) await loadPrompts() } catch (e) { alert('Fehler: ' + e.message) } } const handleDelete = async (prompt) => { if (!confirm(`Prompt "${prompt.name}" wirklich löschen?`)) return try { await api.deletePrompt(prompt.id) await loadPrompts() } catch (e) { alert('Fehler: ' + e.message) } } const handleDuplicate = async (prompt) => { try { await api.duplicatePrompt(prompt.id) await loadPrompts() } catch (e) { alert('Fehler: ' + e.message) } } const handleConvertToBase = async (prompt) => { // Convert a 1-stage pipeline to a base prompt if (prompt.type !== 'pipeline') { alert('Nur Pipeline-Prompts können konvertiert werden') return } const stages = typeof prompt.stages === 'string' ? JSON.parse(prompt.stages) : prompt.stages if (!stages || stages.length !== 1) { alert('Nur 1-stage Pipeline-Prompts können zu Basis-Prompts konvertiert werden') return } const stage1 = stages[0] if (!stage1.prompts || stage1.prompts.length !== 1) { alert('Stage muss genau einen Prompt haben') return } const firstPrompt = stage1.prompts[0] if (firstPrompt.source !== 'inline' || !firstPrompt.template) { alert('Nur inline Templates können konvertiert werden') return } if (!confirm(`"${prompt.name}" zu Basis-Prompt konvertieren?`)) return try { await api.updateUnifiedPrompt(prompt.id, { type: 'base', template: firstPrompt.template, output_format: firstPrompt.output_format || 'text', stages: null }) await loadPrompts() } catch (e) { alert('Fehler: ' + e.message) } } const handleSave = async () => { setEditingPrompt(null) setShowNewPrompt(false) await loadPrompts() } const getStageCount = (prompt) => { if (prompt.type !== 'pipeline' || !prompt.stages) return 0 try { const stages = typeof prompt.stages === 'string' ? JSON.parse(prompt.stages) : prompt.stages return stages.length } catch (e) { return 0 } } const getTypeLabel = (type) => { if (type === 'base') return 'Basis' if (type === 'pipeline') return 'Pipeline' return type || 'Pipeline' // Default for old prompts } const getTypeColor = (type) => { if (type === 'base') return 'var(--accent)' if (type === 'pipeline') return '#6366f1' return 'var(--text3)' } const handleExportAll = async () => { try { const data = await api.exportAllPrompts() const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `all-prompts-${new Date().toISOString().split('T')[0]}.json` document.body.appendChild(a) a.click() document.body.removeChild(a) URL.revokeObjectURL(url) } catch (e) { setError('Export-Fehler: ' + e.message) } } const handleImport = async (event) => { const file = event.target.files[0] if (!file) return setImporting(true) setError(null) setImportResult(null) try { const text = await file.text() const data = JSON.parse(text) // Ask user about overwrite const overwrite = confirm( 'Bestehende Prompts überschreiben?\n\n' + 'JA = Existierende Prompts aktualisieren\n' + 'NEIN = Nur neue Prompts erstellen, Duplikate überspringen' ) const result = await api.importPrompts(data, overwrite) setImportResult(result) await loadPrompts() } catch (e) { setError('Import-Fehler: ' + e.message) } finally { setImporting(false) event.target.value = '' // Reset file input } } return (

KI-Prompts ({filteredPrompts.length})

{error && (
{error}
)} {importResult && (
✅ Import erfolgreich
{importResult.created} erstellt · {importResult.updated} aktualisiert · {importResult.skipped} übersprungen
)} {/* Filters */}
Typ:
{/* Prompts Table */} {loading ? (
Lädt...
) : (
{filteredPrompts.length === 0 ? ( ) : ( filteredPrompts.map(prompt => ( )) )}
Typ Name Kategorie Stages Status Aktionen
Keine Prompts gefunden
{getTypeLabel(prompt.type)}
{prompt.display_name || prompt.name}
{prompt.slug}
{prompt.category || 'ganzheitlich'} {prompt.type === 'pipeline' ? ( {getStageCount(prompt)} Stages ) : ( )}
{/* Show convert button for 1-stage pipelines */} {prompt.type === 'pipeline' && getStageCount(prompt) === 1 && ( )}
)} {/* Unified Prompt Modal */} {(editingPrompt || showNewPrompt) && ( { setEditingPrompt(null) setShowNewPrompt(false) }} /> )}
) }