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