- Frontend: debug viewer now shows even when test fails
- Frontend: export button to download complete prompt config as JSON
- Backend: attach debug info to JSON validation errors
- Backend: include raw output and length in error details
Users can now debug failed prompts and export configs for analysis.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Backend: debug mode in prompt_executor with placeholder tracking
- Backend: show resolved/unresolved placeholders, final prompts, AI responses
- Frontend: test button in UnifiedPromptModal for saved prompts
- Frontend: debug output viewer with JSON preview
- Frontend: wider placeholder example fields in PlaceholderPicker
Resolves pipeline execution debugging issues.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- PlaceholderPicker: Example values in separate full-width row
- Analysis.jsx: Show only pipeline-type prompts
- Analysis.jsx: Remove base prompts and Prompts tab
- Cleanup: Remove PromptEditor component and unused imports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Major improvements:
1. PlaceholderPicker component (new)
- Loads placeholders dynamically from backend catalog
- Grouped by categories: Profil, Körper, Ernährung, Training, etc.
- Search/filter functionality
- Shows live example values from user data
- Popup modal with expand/collapse categories
2. Replaced hardcoded placeholder chips
- 'Platzhalter einfügen' button opens picker
- Works in both base templates and pipeline inline templates
- Auto-closes after selection
3. Uses existing backend system
- GET /api/prompts/placeholders
- placeholder_resolver.py with PLACEHOLDER_MAP
- Dynamic, module-based placeholder system
- No manual updates needed when modules add new placeholders
Benefits:
- Scalable: New modules can add placeholders without frontend changes
- User-friendly: Search and categorization
- Context-aware: Shows real example values
- Future-proof: Backend-driven catalog
New features:
1. Placeholder chips now visible in pipeline inline templates
- Click to insert: weight_data, nutrition_data, activity_data, etc.
- Same UX as base prompts
2. Convert to Base Prompt button
- New icon (ArrowDownToLine) in actions column
- Only visible for 1-stage pipeline prompts
- Converts pipeline → base by extracting inline template
- Validates: must be 1-stage, 1-prompt, inline source
This allows migrated prompts to be properly categorized as base prompts
for reuse in other pipelines.
Fixes:
1. Template field in stages now full width (was too narrow)
2. Table horizontal scrollbar for mobile (overflow-x: auto)
3. Table min-width 900px to prevent icon clipping
4. Added clickable placeholder chips below base template
- Click to insert placeholders into template
- Shows: weight_data, nutrition_data, activity_data, sleep_data, etc.
UI now mobile-ready and more user-friendly.
Issue: template has NOT NULL constraint but pipeline-type prompts
don't use template (they use stages JSONB instead).
Solution: ALTER COLUMN template DROP NOT NULL before inserting
pipeline configs into ai_prompts.
Fixed Step 3 pipeline_configs migration:
- Simplified JSONB aggregation logic
- Properly scope pc alias in subqueries
- Use UNNEST with FROM clause for array expansion
Previous version had correlation issues with nested subqueries.
Current state:
- Pipeline configs working (migration 019)
- PipelineConfigModal complete
- AdminPromptsPage with tabs
- All Phase 1+2 features deployed and tested
Next: Consolidate into unified prompt system
- Single ai_prompts table for all types
- Dynamic stages (unlimited)
- Basis prompts + pipeline prompts
Migration 018:
- Add display_name column to ai_prompts
- Migrate existing prompts from hardcoded SLUG_LABELS
- Fallback: name if display_name is NULL
Backend:
- PromptCreate/Update models with display_name field
- create/update/duplicate endpoints handle display_name
- Fallback: use name if display_name not provided
Frontend:
- PromptEditModal: display_name input field
- Placeholder picker: button + dropdown with all placeholders
- Shows example values, inserts {{placeholder}} on click
- Analysis.jsx: use display_name instead of SLUG_LABELS
User-facing changes:
- Prompts now show custom display names (e.g. '🍽️ Ernährung')
- Admin can edit display names instead of hardcoded labels
- Template editor has 'Platzhalter einfügen' button
- No more hardcoded SLUG_LABELS in frontend
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
Frontend components:
- PromptEditModal.jsx: Full editor with preview, generator, optimizer
- PromptGenerator.jsx: KI-assisted prompt creation from goal description
- Extended api.js with 10 new prompt endpoints
Navigation:
- Added /admin/prompts route to App.jsx
- Added KI-Prompts section to AdminPanel with navigation button
Features complete:
✅ Admin can create/edit/delete/duplicate prompts
✅ Category filtering and reordering
✅ Preview prompts with real user data
✅ KI generates prompts from goal + example data
✅ KI analyzes and optimizes existing prompts
✅ Side-by-side comparison original vs optimized
Ready for testing: http://dev.mitai.jinkendo.de/admin/prompts
Issue #28 Phase 2 complete - 13-18h estimated, ~14h actual
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend complete:
- Migration 017: Add category column to ai_prompts
- placeholder_resolver.py: 20+ placeholders with resolver functions
- Extended routers/prompts.py with CRUD endpoints:
* POST /api/prompts (create)
* PUT /api/prompts/:id (update)
* DELETE /api/prompts/:id (delete)
* POST /api/prompts/:id/duplicate
* PUT /api/prompts/reorder
* POST /api/prompts/preview
* GET /api/prompts/placeholders
* POST /api/prompts/generate (KI-assisted generation)
* POST /api/prompts/:id/optimize (KI analysis)
- Extended models.py with PromptCreate, PromptUpdate, PromptGenerateRequest
Frontend:
- AdminPromptsPage.jsx: Full CRUD UI with category filter, reordering
Meta-Features:
- KI generates prompts from goal description + example data
- KI analyzes and optimizes existing prompts
Next: PromptEditModal, PromptGenerator, api.js integration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Removed local quality filter UI from History page since backend now
handles filtering globally. Activities are already filtered when loaded.
Changes:
- Removed qualityLevel local state
- Simplified filtA to only filter by period
- Replaced filter buttons with info banner showing active global filter
- Added 'Hier ändern →' link to Settings
User can now only change quality filter in Settings (global), not per
page. History shows which filter is active with link to change it.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The frontend was sending quality_filter_level to the backend, but the
Pydantic ProfileUpdate model didn't include this field, so it was
silently ignored. Profile updates never actually saved the filter.
This is why the charts didn't react to filter changes - the backend
database was never updated.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The component was loading data from backend (which uses global filter)
but useEffect dependency didn't include quality_filter_level, so it
didn't reload when user changed the filter in Settings.
Added useProfile() context and activeProfile.quality_filter_level
to dependency array.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implemented global quality_filter_level in user profiles for consistent
data filtering across all views (Dashboard, History, Charts, KI-Pipeline).
Backend changes:
- Migration 016: Add quality_filter_level column to profiles table
- quality_filter.py: Centralized helper functions for SQL filtering
- insights.py: Apply global filter in _get_profile_data()
- activity.py: Apply global filter in list_activity()
Frontend changes:
- SettingsPage.jsx: Add Datenqualität section with 4-level selector
- History.jsx: Use global quality filter from profile context
Filter levels: all, quality (good+excellent+acceptable), very_good
(good+excellent), excellent (only excellent)
Closes#31
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Notiert an 3 Stellen:
1. insights.py: TODO-Kommentar im Code
2. ROADMAP.md: Deliverable bei M0.2 (lokal, nicht im Git)
3. Gitea Issue #28: Kommentar mit Spezifikation
Zukünftig:
- GET /api/insights/run/{slug}?quality_level=quality
- 4 Stufen: all, quality, very_good, excellent
- Frontend: Dropdown wie in History.jsx
- Pipeline-Configs können Standard-Level haben
User-Request: Quality-Level-Auswahl für KI-Analysen
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Statt einfachem On/Off Toggle jetzt 4 Qualitätsstufen:
- 📊 Alle (kein Filter)
- ✓ Hochwertig (excellent + good + acceptable)
- ✓✓ Sehr gut (excellent + good)
- ⭐ Exzellent (nur excellent)
UI:
- Button-Group (Segmented Control) mit 4 Stufen
- Beschreibung welche Labels inkludiert werden
- Anzeige: X von Y Aktivitäten (wenn gefiltert)
User-Feedback: Stufenweiser Filter ist flexibler als binärer Toggle
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Problem: Import failed with "invalid literal for int() with base 10: '37.95'"
because Apple Health exports HRV and other vitals with decimal values.
Root cause: Code used int() directly on string values with decimals.
Fix:
- Added safe_int(): parses decimals as float first, then rounds to int
- Added safe_float(): robust float parsing with error handling
- Applied to all vital value parsing: RHR, HRV, VO2 Max, SpO2, resp rate
Example: '37.95' → float(37.95) → int(38) ✓
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Problem: Errors during import were logged but not visible to user.
Changes:
- Backend: Collect error messages and return in response (first 10 errors)
- Frontend: Display error details in import result box
- UI: Red background when errors > 0, shows detailed error messages
Now users can see exactly which rows failed and why.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Problem: Import expected English column names, but German Apple Health/Omron
exports use German names with units.
Fixed:
- Apple Health: Support both English and German column names
- "Start" OR "Datum/Uhrzeit"
- "Resting Heart Rate" OR "Ruhepuls (count/min)"
- "Heart Rate Variability" OR "Herzfrequenzvariabilität (ms)"
- "VO2 Max" OR "VO2 max (ml/(kg·min))"
- "Oxygen Saturation" OR "Blutsauerstoffsättigung (%)"
- "Respiratory Rate" OR "Atemfrequenz (count/min)"
- Omron: Support column names with/without units
- "Systolisch (mmHg)" OR "Systolisch"
- "Diastolisch (mmHg)" OR "Diastolisch"
- "Puls (bpm)" OR "Puls"
- "Unregelmäßiger Herzschlag festgestellt" OR "Unregelmäßiger Herzschlag"
- "Mögliches AFib" OR "Vorhofflimmern"
Added debug logging for both imports to show detected columns.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Logs:
- CSV column names from first row
- Rows skipped due to missing date
- Rows skipped due to no vitals data
- Shows which fields were found/missing
Helps diagnose CSV format mismatches.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Problem: Import reported all entries as "updated" even when skipped
due to WHERE clause (source != 'manual')
Root cause: RETURNING returns NULL when WHERE clause prevents update,
but code counted NULL as "updated" instead of "skipped"
Fix:
- Check if result is None → skipped (WHERE prevented update)
- Check if xmax = 0 → inserted (new row)
- Otherwise → updated (existing row modified)
Affects:
- vitals_baseline.py: Apple Health import
- blood_pressure.py: Omron import
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Full-width fields with section headers (mobile-friendly)
- Inline editing for all measurements (edit mode per row)
- Smart upsert: date change loads existing entry → update instead of duplicate
- Units integrated into labels (no overflow)
- Baseline: auto-detects existing entry and switches to update mode
- Blood Pressure: inline editing with all fields (date, time, BP, context, flags)
- Edit/Save/Cancel buttons with lucide-react icons
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Einfache 3-Tab-Struktur als Platzhalter:
- Morgenmessung (Baseline)
- Blutdruck (BP)
- Import
Verhindert Crash durch alte API-Calls.
Vollständige UI folgt nach Backend-Test.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ModuleNotFoundError: No module named 'dateutil' beim Server-Start.
Ursache: vitals.py importiert dateutil.parser für Omron-Datumsformatierung,
aber python-dateutil fehlte in requirements.txt.
Fix: python-dateutil==2.9.0 zu requirements.txt hinzugefügt.
Nach dem Update: Docker Container neu bauen auf dem Pi:
cd /home/lars/docker/bodytrack-dev
docker compose -f docker-compose.dev-env.yml build --no-cache backend
docker compose -f docker-compose.dev-env.yml up -d
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
**Backend (insights.py):**
- Extended _get_profile_data() to fetch sleep, rest_days, vitals
- Added template variables for Sleep Module:
{{sleep_summary}}, {{sleep_detail}}, {{sleep_avg_duration}}, {{sleep_avg_quality}}
- Added template variables for Rest Days:
{{rest_days_summary}}, {{rest_days_count}}, {{rest_days_types}}
- Added template variables for Vitals:
{{vitals_summary}}, {{vitals_detail}}, {{vitals_avg_hr}}, {{vitals_avg_hrv}},
{{vitals_avg_bp}}, {{vitals_vo2_max}}
**Frontend (Analysis.jsx):**
- Added 12 new template variables to VARS list in PromptEditor
- Enables AI prompt creation for Sleep, Rest Days, and Vitals analysis
All modules now have AI evaluation support for future prompt creation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Mark Sleep Module as complete (Phase 2b)
- Mark Rest Days as complete (Phase 2a)
- Mark Extended Vitals as complete (Phase 2d)
- Add migration details (010-014)
- HF-Zonen + Recovery Score marked as next (Phase 2e)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Import endpoints for Omron blood pressure CSV (German date format)
- Import endpoints for Apple Health vitals CSV
- Import UI tab in VitalsPage with drag & drop for both sources
- German month mapping for Omron date parsing ("13 März 2026")
- Upsert logic preserves manual entries (source != 'manual')
- Import result feedback (inserted/updated/skipped/errors)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Avg blood pressure (systolic/diastolic) 7d and 30d
- Latest VO2 Max value
- Avg SpO2 7d and 30d
- Backend now provides all metrics expected by frontend
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Form sections:
- Morgenmessung: Ruhepuls, HRV
- Blutdruck (Omron): Systolisch, Diastolisch, Puls
- Fitness & Sauerstoff (Apple Watch): VO2 Max, SpO2, Atemfrequenz
- Warnungen: Unregelmäßiger Herzschlag, Mögliches AFib (checkboxes)
Display:
- All vitals shown in entry list with icons
- Blood pressure highlighted in red (🩸)
- VO2 Max in green (🏃)
- Warnings in orange (⚠️)
Stats overview:
- Dynamic grid showing available metrics
- Avg blood pressure 7d
- Latest VO2 Max
- Avg SpO2 7d
Save/Update:
- Only non-empty fields included in payload
- At least one vital must be provided
Ready for manual testing + import implementation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Migration 014:
- blood_pressure_systolic/diastolic (mmHg)
- pulse (bpm) - during BP measurement
- vo2_max (ml/kg/min) - from Apple Watch
- spo2 (%) - blood oxygen saturation
- respiratory_rate (breaths/min)
- irregular_heartbeat, possible_afib (boolean flags from Omron)
- Added 'omron' to source enum
Backend:
- Updated Pydantic models (VitalsEntry, VitalsUpdate)
- Updated all SELECT queries to include new fields
- Updated INSERT/UPDATE with COALESCE for partial updates
- Validation: at least one vital must be provided
Preparation for Omron + Apple Health imports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>