diff --git a/frontend/src/pages/ActivityPage.jsx b/frontend/src/pages/ActivityPage.jsx index 9cc9b6c..3257180 100644 --- a/frontend/src/pages/ActivityPage.jsx +++ b/frontend/src/pages/ActivityPage.jsx @@ -2,6 +2,7 @@ import { useState, useEffect, useRef } from 'react' import { Upload, Pencil, Trash2, Check, X, CheckCircle } from 'lucide-react' import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid } from 'recharts' import { api } from '../utils/api' +import UsageBadge from '../components/UsageBadge' import dayjs from 'dayjs' import 'dayjs/locale/de' dayjs.locale('de') @@ -79,7 +80,7 @@ function ImportPanel({ onImported }) { } // ── Manual Entry ────────────────────────────────────────────────────────────── -function EntryForm({ form, setForm, onSave, onCancel, saveLabel='Speichern' }) { +function EntryForm({ form, setForm, onSave, onCancel, saveLabel='Speichern', saving=false, error=null, usage=null }) { const set = (k,v) => setForm(f=>({...f,[k]:v})) return (
@@ -130,8 +131,25 @@ function EntryForm({ form, setForm, onSave, onCancel, saveLabel='Speichern' }) { value={form.notes||''} onChange={e=>set('notes',e.target.value)}/>
+ {error && ( +
+ {error} +
+ )}
- +
+ +
{onCancel && }
@@ -145,25 +163,51 @@ export default function ActivityPage() { const [tab, setTab] = useState('list') const [form, setForm] = useState(empty()) const [editing, setEditing] = useState(null) + const [saving, setSaving] = useState(false) const [saved, setSaved] = useState(false) + const [error, setError] = useState(null) + const [activityUsage, setActivityUsage] = useState(null) // Phase 4: Usage badge const load = async () => { const [e, s] = await Promise.all([api.listActivity(), api.activityStats()]) setEntries(e); setStats(s) } - useEffect(()=>{ load() },[]) + + const loadUsage = () => { + api.getFeatureUsage().then(features => { + const activityFeature = features.find(f => f.feature_id === 'activity_entries') + setActivityUsage(activityFeature) + }).catch(err => console.error('Failed to load usage:', err)) + } + + useEffect(()=>{ + load() + loadUsage() + },[]) const handleSave = async () => { - const payload = {...form} - if(payload.duration_min) payload.duration_min = parseFloat(payload.duration_min) - if(payload.kcal_active) payload.kcal_active = parseFloat(payload.kcal_active) - if(payload.hr_avg) payload.hr_avg = parseFloat(payload.hr_avg) - if(payload.hr_max) payload.hr_max = parseFloat(payload.hr_max) - if(payload.rpe) payload.rpe = parseInt(payload.rpe) - payload.source = 'manual' - await api.createActivity(payload) - setSaved(true); await load() - setTimeout(()=>{ setSaved(false); setForm(empty()) }, 1500) + setSaving(true) + setError(null) + try { + const payload = {...form} + if(payload.duration_min) payload.duration_min = parseFloat(payload.duration_min) + if(payload.kcal_active) payload.kcal_active = parseFloat(payload.kcal_active) + if(payload.hr_avg) payload.hr_avg = parseFloat(payload.hr_avg) + if(payload.hr_max) payload.hr_max = parseFloat(payload.hr_max) + if(payload.rpe) payload.rpe = parseInt(payload.rpe) + payload.source = 'manual' + await api.createActivity(payload) + setSaved(true) + await load() + await loadUsage() // Reload usage after save + setTimeout(()=>{ setSaved(false); setForm(empty()) }, 1500) + } catch (err) { + console.error('Save failed:', err) + setError(err.message || 'Fehler beim Speichern') + setTimeout(()=>setError(null), 5000) + } finally { + setSaving(false) + } } const handleUpdate = async () => { @@ -225,9 +269,13 @@ export default function ActivityPage() { {tab==='add' && (
-
Training eintragen
+
+ Training eintragen + {activityUsage && } +
+ onSave={handleSave} saveLabel={saved?'✓ Gespeichert!':'Speichern'} + saving={saving} error={error} usage={activityUsage}/>
)} diff --git a/frontend/src/pages/Analysis.jsx b/frontend/src/pages/Analysis.jsx index c3e3789..5d50ad2 100644 --- a/frontend/src/pages/Analysis.jsx +++ b/frontend/src/pages/Analysis.jsx @@ -254,12 +254,22 @@ export default function Analysis() { )} - +
+ +
{!canUseAI &&
🔒 KI nicht freigeschaltet
} {pipelineLoading && ( @@ -306,12 +316,22 @@ export default function Analysis() { )} - +
+ +
{/* Show existing result collapsed */} {existing && newResult?.id !== existing.id && (