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() {
)}
-