Goalsystem V1 #50
425
docs/issues/issue-51-prompt-page-assignment.md
Normal file
425
docs/issues/issue-51-prompt-page-assignment.md
Normal file
|
|
@ -0,0 +1,425 @@
|
||||||
|
# Feature: Prompt-Zuordnung zu Verlaufsseiten
|
||||||
|
|
||||||
|
**Labels:** feature, ux, enhancement
|
||||||
|
**Priority:** Medium (Phase 1-2)
|
||||||
|
**Related:** Issue #28 (Unified Prompt System - Complete)
|
||||||
|
|
||||||
|
## Beschreibung
|
||||||
|
KI-Prompts sollen flexibel auf verschiedenen Verlaufsseiten verfügbar gemacht werden können. Jeder Prompt kann auf mehreren Seiten gleichzeitig angeboten werden (Mehrfachauswahl).
|
||||||
|
|
||||||
|
## Problem (aktueller Stand)
|
||||||
|
|
||||||
|
**Aktuell:**
|
||||||
|
- Prompts sind nur über die zentrale Analyse-Seite (📊 Analyse) verfügbar
|
||||||
|
- Kein kontextbezogener Zugriff auf relevante Analysen
|
||||||
|
- User muss immer zur Analyse-Seite navigieren
|
||||||
|
|
||||||
|
**Beispiel-Szenario:**
|
||||||
|
```
|
||||||
|
User ist auf: Gewicht → Verlauf
|
||||||
|
Will: Gewichtstrend analysieren
|
||||||
|
Muss: Zur Analyse-Seite → Prompt auswählen → Zurück
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wünschenswert:**
|
||||||
|
```
|
||||||
|
User ist auf: Gewicht → Verlauf
|
||||||
|
Sieht: "🤖 KI-Analyse" Button mit relevanten Prompts
|
||||||
|
Kann: Direkt "Gewichtstrend-Analyse" starten
|
||||||
|
```
|
||||||
|
|
||||||
|
## Gewünschtes Verhalten
|
||||||
|
|
||||||
|
### 1. Prompt-Konfiguration erweitern
|
||||||
|
|
||||||
|
**Admin → KI-Prompts → Prompt bearbeiten:**
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────┐
|
||||||
|
│ Prompt bearbeiten: Gewichtstrend-Analyse │
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ Name: Gewichtstrend-Analyse │
|
||||||
|
│ Slug: weight_trend │
|
||||||
|
│ Type: Pipeline │
|
||||||
|
│ │
|
||||||
|
│ 📍 Verfügbar auf Seiten: │
|
||||||
|
│ ┌─────────────────────────────────────────┐ │
|
||||||
|
│ │ ☑ Analyse (Hauptseite) │ │
|
||||||
|
│ │ ☑ Gewicht → Verlauf │ │
|
||||||
|
│ │ ☐ Umfänge → Verlauf │ │
|
||||||
|
│ │ ☐ Caliper → Verlauf │ │
|
||||||
|
│ │ ☐ Aktivität → Verlauf │ │
|
||||||
|
│ │ ☐ Ernährung → Verlauf │ │
|
||||||
|
│ │ ☐ Schlaf → Verlauf │ │
|
||||||
|
│ │ ☐ Vitalwerte → Verlauf │ │
|
||||||
|
│ │ ☐ Dashboard │ │
|
||||||
|
│ └─────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ [Speichern] [Abbrechen] │
|
||||||
|
└─────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mehrfachauswahl:**
|
||||||
|
- Ein Prompt kann auf mehreren Seiten gleichzeitig verfügbar sein
|
||||||
|
- Mindestens eine Seite muss ausgewählt sein
|
||||||
|
- Default: "Analyse (Hauptseite)" ist immer vorausgewählt
|
||||||
|
|
||||||
|
### 2. UI auf Verlaufsseiten
|
||||||
|
|
||||||
|
**Gewicht → Verlauf:**
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────┐
|
||||||
|
│ 📊 Gewicht - Verlauf │
|
||||||
|
│ [Filter: 7d] [30d] [90d] [Alle] │
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ [Chart: Gewichtsverlauf] │
|
||||||
|
│ │
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ 🤖 KI-Analysen │
|
||||||
|
│ ┌─────────────────────────────────────────┐ │
|
||||||
|
│ │ Gewichtstrend-Analyse [▶ Starten]│ │
|
||||||
|
│ │ Körperkomposition-Check [▶ Starten]│ │
|
||||||
|
│ └─────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ [Einträge-Tabelle...] │
|
||||||
|
└─────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Kompaktes Widget unterhalb des Charts
|
||||||
|
- Nur relevante Prompts werden angezeigt
|
||||||
|
- Button startet Analyse inline (Modal oder expandierend)
|
||||||
|
- Ergebnis wird direkt auf der Seite angezeigt
|
||||||
|
|
||||||
|
### 3. Inline-Analyse Anzeige
|
||||||
|
|
||||||
|
**Option A: Modal (empfohlen für MVP):**
|
||||||
|
```
|
||||||
|
Click auf [▶ Starten]
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────┐
|
||||||
|
│ ✕ Gewichtstrend-Analyse │
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ [Spinner] Analysiere Gewichtsdaten... │
|
||||||
|
│ │
|
||||||
|
│ [Nach Abschluss:] │
|
||||||
|
│ Analyse-Text... │
|
||||||
|
│ │
|
||||||
|
│ 📊 Verwendete Werte (12) [🔬 Experten] │
|
||||||
|
│ [Value Table...] │
|
||||||
|
│ │
|
||||||
|
│ [Schließen] [In Verlauf speichern] │
|
||||||
|
└─────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option B: Expandierend (später):**
|
||||||
|
```
|
||||||
|
Click auf [▶ Starten]
|
||||||
|
↓
|
||||||
|
Widget expandiert nach unten
|
||||||
|
Zeigt Analyse-Ergebnis inline
|
||||||
|
[△ Einklappen] Button
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technische Umsetzung
|
||||||
|
|
||||||
|
### 1. Datenbankschema erweitern
|
||||||
|
|
||||||
|
**Tabelle: `ai_prompts`**
|
||||||
|
```sql
|
||||||
|
ALTER TABLE ai_prompts ADD COLUMN available_on JSONB DEFAULT '["analysis"]';
|
||||||
|
|
||||||
|
COMMENT ON COLUMN ai_prompts.available_on IS
|
||||||
|
'Array of page slugs where prompt is available.
|
||||||
|
Values: analysis, weight_history, circ_history, caliper_history,
|
||||||
|
activity_history, nutrition_history, sleep_history, vitals_history, dashboard';
|
||||||
|
|
||||||
|
-- Migration 022
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beispiel-Werte:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"slug": "weight_trend",
|
||||||
|
"name": "Gewichtstrend-Analyse",
|
||||||
|
"available_on": ["analysis", "weight_history"]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
"slug": "pipeline_master",
|
||||||
|
"name": "Vollständige Analyse",
|
||||||
|
"available_on": ["analysis", "dashboard"]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
"slug": "nutrition_check",
|
||||||
|
"name": "Ernährungs-Check",
|
||||||
|
"available_on": ["analysis", "nutrition_history", "activity_history"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Backend API erweitern
|
||||||
|
|
||||||
|
**Neuer Endpoint: GET /api/prompts/for-page/{page_slug}**
|
||||||
|
|
||||||
|
```python
|
||||||
|
@router.get("/for-page/{page_slug}")
|
||||||
|
def get_prompts_for_page(page_slug: str, session: dict = Depends(require_auth)):
|
||||||
|
"""Get all prompts available for a specific page.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
page_slug: Page identifier (e.g., 'weight_history', 'analysis')
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of prompts with available_on containing page_slug
|
||||||
|
"""
|
||||||
|
with get_db() as conn:
|
||||||
|
cur = get_cursor(conn)
|
||||||
|
cur.execute(
|
||||||
|
"""SELECT id, name, slug, type, description, available_on
|
||||||
|
FROM ai_prompts
|
||||||
|
WHERE available_on @> %s
|
||||||
|
ORDER BY name""",
|
||||||
|
(json.dumps([page_slug]),)
|
||||||
|
)
|
||||||
|
return [r2d(row) for row in cur.fetchall()]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beispiel-Aufruf:**
|
||||||
|
```javascript
|
||||||
|
// In WeightPage.jsx
|
||||||
|
const prompts = await api.getPromptsForPage('weight_history')
|
||||||
|
// Returns: [{slug: 'weight_trend', name: 'Gewichtstrend-Analyse', ...}]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Prompt CRUD erweitern:**
|
||||||
|
```python
|
||||||
|
@router.put("/unified/{id}")
|
||||||
|
def update_unified_prompt(id: str, p: UnifiedPromptCreate, session=Depends(require_admin)):
|
||||||
|
# ... existing code ...
|
||||||
|
cur.execute(
|
||||||
|
"""UPDATE ai_prompts
|
||||||
|
SET name=%s, slug=%s, template=%s, ..., available_on=%s
|
||||||
|
WHERE id=%s""",
|
||||||
|
(..., json.dumps(p.available_on), id)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Frontend: Prompt-Editor erweitern
|
||||||
|
|
||||||
|
**UnifiedPromptModal.jsx:**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const PAGE_OPTIONS = [
|
||||||
|
{ value: 'analysis', label: '📊 Analyse (Hauptseite)', default: true },
|
||||||
|
{ value: 'weight_history', label: '⚖️ Gewicht → Verlauf' },
|
||||||
|
{ value: 'circ_history', label: '📏 Umfänge → Verlauf' },
|
||||||
|
{ value: 'caliper_history', label: '📐 Caliper → Verlauf' },
|
||||||
|
{ value: 'activity_history', label: '🏃 Aktivität → Verlauf' },
|
||||||
|
{ value: 'nutrition_history', label: '🍎 Ernährung → Verlauf' },
|
||||||
|
{ value: 'sleep_history', label: '😴 Schlaf → Verlauf' },
|
||||||
|
{ value: 'vitals_history', label: '❤️ Vitalwerte → Verlauf' },
|
||||||
|
{ value: 'dashboard', label: '🏠 Dashboard' },
|
||||||
|
]
|
||||||
|
|
||||||
|
// In form:
|
||||||
|
<div className="form-row">
|
||||||
|
<label>Verfügbar auf Seiten</label>
|
||||||
|
<div style={{display: 'flex', flexDirection: 'column', gap: 8}}>
|
||||||
|
{PAGE_OPTIONS.map(opt => (
|
||||||
|
<label key={opt.value} style={{display: 'flex', gap: 8, alignItems: 'center'}}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={availableOn.includes(opt.value)}
|
||||||
|
onChange={e => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
setAvailableOn([...availableOn, opt.value])
|
||||||
|
} else {
|
||||||
|
// Don't allow unchecking all
|
||||||
|
if (availableOn.length > 1) {
|
||||||
|
setAvailableOn(availableOn.filter(v => v !== opt.value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{opt.label}
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Frontend: Verlaufsseiten erweitern
|
||||||
|
|
||||||
|
**WeightPage.jsx (Beispiel):**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function WeightPage() {
|
||||||
|
const [prompts, setPrompts] = useState([])
|
||||||
|
const [runningAnalysis, setRunningAnalysis] = useState(null)
|
||||||
|
const [analysisResult, setAnalysisResult] = useState(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadPrompts()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const loadPrompts = async () => {
|
||||||
|
try {
|
||||||
|
const data = await api.getPromptsForPage('weight_history')
|
||||||
|
setPrompts(data)
|
||||||
|
} catch(e) {
|
||||||
|
console.error('Failed to load prompts:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const runAnalysis = async (promptSlug) => {
|
||||||
|
setRunningAnalysis(promptSlug)
|
||||||
|
try {
|
||||||
|
const result = await api.executePrompt(promptSlug, {save: true})
|
||||||
|
setAnalysisResult(result)
|
||||||
|
} catch(e) {
|
||||||
|
setError(e.message)
|
||||||
|
} finally {
|
||||||
|
setRunningAnalysis(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="page">
|
||||||
|
<h1>Gewicht - Verlauf</h1>
|
||||||
|
|
||||||
|
{/* Chart */}
|
||||||
|
<WeightChart data={data} />
|
||||||
|
|
||||||
|
{/* AI Prompts Widget */}
|
||||||
|
{prompts.length > 0 && (
|
||||||
|
<div className="ai-prompts-widget">
|
||||||
|
<h3>🤖 KI-Analysen</h3>
|
||||||
|
{prompts.map(p => (
|
||||||
|
<button
|
||||||
|
key={p.slug}
|
||||||
|
onClick={() => runAnalysis(p.slug)}
|
||||||
|
disabled={runningAnalysis === p.slug}
|
||||||
|
>
|
||||||
|
{p.name} {runningAnalysis === p.slug ? '⏳' : '▶'}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Analysis Result Modal */}
|
||||||
|
{analysisResult && (
|
||||||
|
<AnalysisResultModal
|
||||||
|
result={analysisResult}
|
||||||
|
onClose={() => setAnalysisResult(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Data Table */}
|
||||||
|
<DataTable entries={entries} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wiederverwendbare Komponente:**
|
||||||
|
```javascript
|
||||||
|
// components/PagePrompts.jsx
|
||||||
|
export function PagePrompts({ pageSlug }) {
|
||||||
|
// ... logic ...
|
||||||
|
return (
|
||||||
|
<div className="page-prompts">
|
||||||
|
<h3>🤖 KI-Analysen</h3>
|
||||||
|
{prompts.map(p => (
|
||||||
|
<PromptButton key={p.slug} prompt={p} onRun={runAnalysis} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage in any page:
|
||||||
|
<PagePrompts pageSlug="weight_history" />
|
||||||
|
```
|
||||||
|
|
||||||
|
## Akzeptanzkriterien
|
||||||
|
|
||||||
|
- [ ] DB-Migration: `available_on` JSONB column in ai_prompts
|
||||||
|
- [ ] Backend: `GET /api/prompts/for-page/{page_slug}` Endpoint
|
||||||
|
- [ ] Backend: CRUD operations unterstützen available_on
|
||||||
|
- [ ] Frontend: Prompt-Editor zeigt Page-Auswahl (Mehrfachauswahl)
|
||||||
|
- [ ] Frontend: Mindestens 1 Page muss ausgewählt sein
|
||||||
|
- [ ] Frontend: Wiederverwendbare PagePrompts Komponente
|
||||||
|
- [ ] Frontend: Integration in mind. 2 Verlaufsseiten (Weight, Nutrition)
|
||||||
|
- [ ] UI: Inline-Analyse via Modal mit Value Table
|
||||||
|
- [ ] UI: Loading-State während Analyse läuft
|
||||||
|
- [ ] Dokumentation: API-Dokumentation aktualisiert
|
||||||
|
|
||||||
|
## Abschätzung
|
||||||
|
|
||||||
|
**Aufwand:** 6-8 Stunden
|
||||||
|
- 1h: DB-Migration + Backend Endpoint
|
||||||
|
- 2h: Prompt-Editor erweitern (Page-Auswahl)
|
||||||
|
- 2h: PagePrompts Komponente + Modal
|
||||||
|
- 2h: Integration in Verlaufsseiten (2-3 Seiten)
|
||||||
|
- 1h: Testing + Feintuning
|
||||||
|
|
||||||
|
**Priorität:** Medium
|
||||||
|
- Verbessert UX erheblich (kontextbezogene Analysen)
|
||||||
|
- Nutzt bestehendes Prompt-System (Issue #28)
|
||||||
|
- Relativ einfach zu implementieren (kein neues Backend-System)
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
|
||||||
|
### UC1: Gewichtstrend auf Gewicht-Seite
|
||||||
|
```
|
||||||
|
User: Navigiert zu "Gewicht → Verlauf"
|
||||||
|
System: Zeigt Gewichts-Chart + verfügbare Prompts
|
||||||
|
User: Click "Gewichtstrend-Analyse ▶"
|
||||||
|
System: Startet Analyse, zeigt Modal mit Ergebnis
|
||||||
|
User: Click "In Verlauf speichern"
|
||||||
|
System: Speichert in ai_insights, zeigt in Analyse-Verlauf
|
||||||
|
```
|
||||||
|
|
||||||
|
### UC2: Ernährungs-Check auf Ernährung-Seite
|
||||||
|
```
|
||||||
|
User: Navigiert zu "Ernährung → Verlauf"
|
||||||
|
System: Zeigt Ernährungs-Charts + verfügbare Prompts
|
||||||
|
User: Click "Ernährungs-Check ▶"
|
||||||
|
System: Analysiert Makros + Kalorien der letzten 7 Tage
|
||||||
|
User: Sieht Empfehlungen direkt auf Ernährungs-Seite
|
||||||
|
```
|
||||||
|
|
||||||
|
### UC3: Multi-Page Prompt (z.B. "Vollständige Analyse")
|
||||||
|
```
|
||||||
|
Admin: Konfiguriert "Vollständige Analyse"
|
||||||
|
- Verfügbar auf: [Analyse, Dashboard, Gewicht, Ernährung]
|
||||||
|
User: Sieht denselben Prompt auf 4 verschiedenen Seiten
|
||||||
|
User: Kann von überall die gleiche umfassende Analyse starten
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notizen
|
||||||
|
|
||||||
|
- **Rückwärtskompatibilität:** Bestehende Prompts ohne `available_on` → Default `["analysis"]`
|
||||||
|
- **Migration:** Alle existierenden Prompts bekommen `["analysis"]` gesetzt
|
||||||
|
- **Permissions:** Prompts respektieren weiterhin Feature-Enforcement (ai_calls)
|
||||||
|
- **Caching:** Prompts könnten gecacht werden (selten geändert)
|
||||||
|
- **Mobile:** PagePrompts sollte auch auf Mobile gut aussehen (Stack-Layout)
|
||||||
|
- **Performance:** Lazy-Loading der Prompts (nur laden wenn Seite besucht)
|
||||||
|
|
||||||
|
## Erweiterungen (Future)
|
||||||
|
|
||||||
|
- **Conditional Display:** Prompts nur anzeigen wenn Daten vorhanden
|
||||||
|
- Beispiel: "Gewichtstrend" nur wenn min. 3 Gewichts-Einträge
|
||||||
|
- **Quick Actions:** Direkt-Buttons im Chart (ohne separates Widget)
|
||||||
|
- **Page-spezifische Variablen:** Automatisch aktuelle Filter übergeben
|
||||||
|
- Beispiel: Wenn "30d" Filter aktiv → `{{timeframe}}` = 30
|
||||||
|
- **Prompt-Templates pro Page:** Vordefinierte Vorlagen für jede Seite
|
||||||
|
- **Favoriten:** User kann Prompts auf Seiten favorisieren (User-spezifisch)
|
||||||
|
|
||||||
|
## Verwandte Issues
|
||||||
|
|
||||||
|
- #28: Unified Prompt System (Basis für dieses Feature)
|
||||||
|
- #45: KI Prompt-Optimierer (könnte Page-Kontext nutzen)
|
||||||
|
- #46: KI Prompt-Ersteller (sollte Page-Auswahl anbieten)
|
||||||
Loading…
Reference in New Issue
Block a user