2026-03-24 15:35:55 +01:00
|
|
|
|
import { useState } from 'react'
|
|
|
|
|
|
import { api } from '../utils/api'
|
|
|
|
|
|
|
|
|
|
|
|
export default function PromptGenerator({ onGenerated, onClose }) {
|
|
|
|
|
|
const [goal, setGoal] = useState('')
|
|
|
|
|
|
const [dataCategories, setDataCategories] = useState(['körper', 'ernährung'])
|
|
|
|
|
|
const [exampleOutput, setExampleOutput] = useState('')
|
|
|
|
|
|
const [exampleData, setExampleData] = useState(null)
|
|
|
|
|
|
const [generating, setGenerating] = useState(false)
|
|
|
|
|
|
const [loadingExample, setLoadingExample] = useState(false)
|
|
|
|
|
|
|
|
|
|
|
|
const categories = [
|
|
|
|
|
|
{ id: 'körper', label: 'Körper (Gewicht, KF, Umfänge)' },
|
|
|
|
|
|
{ id: 'ernährung', label: 'Ernährung (Kalorien, Makros)' },
|
|
|
|
|
|
{ id: 'training', label: 'Training (Volumen, Typen)' },
|
|
|
|
|
|
{ id: 'schlaf', label: 'Schlaf (Dauer, Qualität)' },
|
|
|
|
|
|
{ id: 'vitalwerte', label: 'Vitalwerte (RHR, HRV, VO2max)' },
|
|
|
|
|
|
{ id: 'ziele', label: 'Ziele (Fortschritt, Prognose)' }
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const handleToggleCategory = (catId) => {
|
|
|
|
|
|
if (dataCategories.includes(catId)) {
|
|
|
|
|
|
setDataCategories(dataCategories.filter(c => c !== catId))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
setDataCategories([...dataCategories, catId])
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleShowExampleData = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
setLoadingExample(true)
|
|
|
|
|
|
const placeholders = await api.listPlaceholders()
|
|
|
|
|
|
setExampleData(placeholders)
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
alert('Fehler: ' + e.message)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setLoadingExample(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleGenerate = async () => {
|
|
|
|
|
|
if (!goal.trim()) {
|
|
|
|
|
|
alert('Bitte Ziel beschreiben')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (dataCategories.length === 0) {
|
|
|
|
|
|
alert('Bitte mindestens einen Datenbereich wählen')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
setGenerating(true)
|
|
|
|
|
|
const result = await api.generatePrompt({
|
|
|
|
|
|
goal,
|
|
|
|
|
|
data_categories: dataCategories,
|
|
|
|
|
|
example_output: exampleOutput || null
|
|
|
|
|
|
})
|
|
|
|
|
|
onGenerated(result)
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
alert('Fehler beim Generieren: ' + e.message)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setGenerating(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div style={{
|
|
|
|
|
|
position:'fixed', inset:0, background:'rgba(0,0,0,0.6)',
|
|
|
|
|
|
display:'flex', alignItems:'center', justifyContent:'center',
|
|
|
|
|
|
zIndex:1001, padding:20
|
|
|
|
|
|
}}>
|
|
|
|
|
|
<div style={{
|
|
|
|
|
|
background:'var(--bg)', borderRadius:12, maxWidth:700, width:'100%',
|
|
|
|
|
|
maxHeight:'90vh', overflow:'auto', padding:24
|
|
|
|
|
|
}}>
|
|
|
|
|
|
<h2 style={{margin:'0 0 24px 0', fontSize:18, fontWeight:600}}>
|
|
|
|
|
|
🤖 KI-Prompt generieren
|
|
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Step 1: Goal */}
|
|
|
|
|
|
<div style={{marginBottom:24}}>
|
2026-03-24 20:53:15 +01:00
|
|
|
|
<label className="form-label" style={{display:'block', marginBottom:6}}>
|
2026-03-24 15:35:55 +01:00
|
|
|
|
1️⃣ Was möchtest du analysieren?
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<textarea
|
|
|
|
|
|
className="form-input"
|
|
|
|
|
|
value={goal}
|
|
|
|
|
|
onChange={e => setGoal(e.target.value)}
|
|
|
|
|
|
rows={3}
|
|
|
|
|
|
placeholder="Beispiel: Ich möchte wissen ob meine Proteinzufuhr ausreichend ist für Muskelaufbau und wie ich sie optimieren kann."
|
2026-03-24 20:53:15 +01:00
|
|
|
|
style={{width:'100%', textAlign:'left', resize:'vertical'}}
|
2026-03-24 15:35:55 +01:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Step 2: Data Categories */}
|
|
|
|
|
|
<div style={{marginBottom:24}}>
|
|
|
|
|
|
<label className="form-label">
|
|
|
|
|
|
2️⃣ Welche Daten sollen analysiert werden?
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:8}}>
|
|
|
|
|
|
{categories.map(cat => (
|
|
|
|
|
|
<label
|
|
|
|
|
|
key={cat.id}
|
|
|
|
|
|
style={{
|
|
|
|
|
|
display:'flex', alignItems:'center', gap:8,
|
|
|
|
|
|
padding:8, background:'var(--surface)', borderRadius:6,
|
|
|
|
|
|
cursor:'pointer', fontSize:13
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="checkbox"
|
|
|
|
|
|
checked={dataCategories.includes(cat.id)}
|
|
|
|
|
|
onChange={() => handleToggleCategory(cat.id)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
{cat.label}
|
|
|
|
|
|
</label>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={handleShowExampleData}
|
|
|
|
|
|
disabled={loadingExample}
|
|
|
|
|
|
style={{
|
|
|
|
|
|
marginTop:12, fontSize:12, padding:'6px 12px',
|
|
|
|
|
|
background:'var(--surface2)', border:'1px solid var(--border)',
|
|
|
|
|
|
borderRadius:6, cursor:'pointer'
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{loadingExample ? 'Lädt...' : '📊 Beispieldaten anzeigen'}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Example Data */}
|
|
|
|
|
|
{exampleData && (
|
|
|
|
|
|
<div style={{
|
|
|
|
|
|
marginBottom:24, padding:12, background:'var(--surface2)',
|
|
|
|
|
|
borderRadius:8, fontSize:11, fontFamily:'monospace',
|
|
|
|
|
|
maxHeight:200, overflow:'auto'
|
|
|
|
|
|
}}>
|
|
|
|
|
|
<strong style={{fontFamily:'var(--font)'}}>Deine aktuellen Daten:</strong>
|
|
|
|
|
|
<pre style={{marginTop:8, whiteSpace:'pre-wrap'}}>
|
|
|
|
|
|
{JSON.stringify(exampleData, null, 2)}
|
|
|
|
|
|
</pre>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{/* Step 3: Desired Format */}
|
|
|
|
|
|
<div style={{marginBottom:24}}>
|
2026-03-24 20:53:15 +01:00
|
|
|
|
<label className="form-label" style={{display:'block', marginBottom:6}}>
|
2026-03-24 15:35:55 +01:00
|
|
|
|
3️⃣ Gewünschtes Antwort-Format (optional)
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<textarea
|
|
|
|
|
|
className="form-input"
|
|
|
|
|
|
value={exampleOutput}
|
|
|
|
|
|
onChange={e => setExampleOutput(e.target.value)}
|
|
|
|
|
|
rows={4}
|
|
|
|
|
|
placeholder={'Beispiel:\n## Analyse\n[Bewertung]\n\n## Empfehlungen\n- Punkt 1\n- Punkt 2'}
|
2026-03-24 20:53:15 +01:00
|
|
|
|
style={{width:'100%', textAlign:'left', fontFamily:'monospace', fontSize:12, resize:'vertical'}}
|
2026-03-24 15:35:55 +01:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Info Box */}
|
|
|
|
|
|
<div style={{
|
|
|
|
|
|
marginBottom:24, padding:12, background:'var(--surface)',
|
|
|
|
|
|
border:'1px solid var(--border)', borderRadius:8, fontSize:12,
|
|
|
|
|
|
color:'var(--text3)'
|
|
|
|
|
|
}}>
|
|
|
|
|
|
<strong style={{color:'var(--text2)'}}>💡 Tipp:</strong> Je präziser deine Zielbeschreibung,
|
|
|
|
|
|
desto besser der generierte Prompt. Die KI wählt automatisch passende Platzhalter
|
|
|
|
|
|
und strukturiert die Analyse optimal.
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Actions */}
|
|
|
|
|
|
<div style={{display:'flex', gap:12}}>
|
|
|
|
|
|
<button
|
|
|
|
|
|
className="btn btn-primary"
|
|
|
|
|
|
onClick={handleGenerate}
|
|
|
|
|
|
disabled={generating || !goal.trim() || dataCategories.length === 0}
|
|
|
|
|
|
style={{flex:1}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{generating ? '⏳ Generiere...' : '🚀 Prompt generieren'}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button className="btn" onClick={onClose}>
|
|
|
|
|
|
Abbrechen
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|