import { useCallback, useEffect, useState } from 'react' import { Navigate } from 'react-router-dom' import { Sparkles } from 'lucide-react' import { useAuth } from '../context/AuthContext' import api from '../utils/api' import AdminPageNav from '../components/AdminPageNav' /** * Pflege von ai_prompts (OpenRouter-Vorlagen) — nur Superadmin. */ export default function AdminAiPromptsPage() { const { user } = useAuth() const isSuperadmin = user?.role === 'superadmin' const [prompts, setPrompts] = useState([]) const [catalog, setCatalog] = useState(null) const [selectedId, setSelectedId] = useState(null) const [detail, setDetail] = useState(null) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [error, setError] = useState('') const [draftName, setDraftName] = useState('') const [draftDesc, setDraftDesc] = useState('') const [draftTemplate, setDraftTemplate] = useState('') const [draftActive, setDraftActive] = useState(true) const [pvTitle, setPvTitle] = useState('Testübung') const [pvGoal, setPvGoal] = useState('
Ziel hier
') const [pvExec, setPvExec] = useState('Ablauf hier
') const [pvHint, setPvHint] = useState('') const [pvFocusId, setPvFocusId] = useState('') const [pvPreview, setPvPreview] = useState(null) const loadList = useCallback(async () => { const [pList, cat] = await Promise.all([ api.listAdminAiPrompts(), api.getAdminAiPromptPlaceholdersCatalog(), ]) setPrompts(Array.isArray(pList) ? pList : []) setCatalog(cat || null) }, []) useEffect(() => { if (!isSuperadmin) return let cancelled = false ;(async () => { setLoading(true) setError('') try { await loadList() } catch (e) { if (!cancelled) setError(e.message || String(e)) } finally { if (!cancelled) setLoading(false) } })() return () => { cancelled = true } }, [isSuperadmin, loadList]) useEffect(() => { if (!isSuperadmin || !selectedId) { setDetail(null) return } let cancelled = false ;(async () => { try { const d = await api.getAdminAiPrompt(selectedId) if (!cancelled) { setDetail(d) setDraftName(d.display_name || '') setDraftDesc(d.description || '') setDraftTemplate(d.template || '') setDraftActive(!!d.active) setPvPreview(null) } } catch (e) { if (!cancelled) setError(e.message || String(e)) } })() return () => { cancelled = true } }, [isSuperadmin, selectedId]) const save = async () => { if (!detail?.id) return setSaving(true) setError('') try { await api.updateAdminAiPrompt(detail.id, { template: draftTemplate, display_name: draftName, description: draftDesc, active: draftActive, }) await loadList() const nd = await api.getAdminAiPrompt(detail.id) setDetail(nd) setPvPreview(null) } catch (e) { setError(e.message || String(e)) } finally { setSaving(false) } } const resetTemplate = async () => { if (!detail?.id || !detail.has_reference_template) return if (!confirm('Template auf gespeicherten Referenztext zurücksetzen?')) return setSaving(true) try { const nd = await api.resetAdminAiPromptTemplate(detail.id) setDetail(nd) setDraftTemplate(nd.template || '') await loadList() } catch (e) { setError(e.message || String(e)) } finally { setSaving(false) } } const runPreview = async () => { if (!detail?.id) return setError('') try { const body = { title: pvTitle, goal: pvGoal, execution: pvExec, focus_hint: pvHint || undefined, } const fid = parseInt(String(pvFocusId).trim(), 10) if (Number.isFinite(fid) && fid >= 1) { body.focus_areas_context = [{ focus_area_id: fid, is_primary: true }] } const r = await api.previewAdminAiPrompt(detail.id, body) setPvPreview(r) } catch (e) { setError(e.message || String(e)) } } if (!isSuperadmin) return
Datenbankvorlagen (ai_prompts) für Übungs-KI. Platzhalter im Mustache-Stil werden serverseitig
aufgelöst — die Vorschau unten ruft kein externes Modell auf.
{error}
: null}Prompt links wählen.
) : ({detail?.slug} · Ausgabe:{' '}
{detail?.output_format} · Kategorie: {detail?.category}
{ph.placeholder} — {ph.description}{' '}
[{Array.isArray(ph.used_by_slugs) ? ph.used_by_slugs.join(', ') : ''}]
Wird geladen …
)}{pvPreview.warning}
) : null} {pvPreview?.placeholders_remaining?.length ? (Unbekannte Platzhalter im Ergebnis:{' '} {pvPreview.placeholders_remaining.join(', ')}
) : null} {pvPreview?.resolved_template != null ? (
{pvPreview.resolved_template}
) : null}