mitai-jinkendo/frontend/src/components/PromptGenerator.jsx
Lars 7a8a5aee98
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
fix: prompt editor layout - full-width inputs, left-aligned text (Issue #28)
- PromptEditModal: all inputs/textareas now full-width
- Labels positioned above fields (not inline)
- Text left-aligned (was right-aligned)
- Added resize:vertical for textareas
- Side-by-side comparison with word-wrap
- Follows app-wide form design pattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 20:53:15 +01:00

191 lines
6.5 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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}}>
<label className="form-label" style={{display:'block', marginBottom:6}}>
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."
style={{width:'100%', textAlign:'left', resize:'vertical'}}
/>
</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}}>
<label className="form-label" style={{display:'block', marginBottom:6}}>
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'}
style={{width:'100%', textAlign:'left', fontFamily:'monospace', fontSize:12, resize:'vertical'}}
/>
</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>
)
}