feat(v9d): integrate training type UI components
Phase 1b - UI Integration: =========================== ActivityPage: - Replace old activity type dropdown with TrainingTypeSelect - Add training_type_id, training_category, training_subcategory to form - Two-level selection (category → subcategory) Dashboard: - Add TrainingTypeDistribution card (pie chart) - Shows last 28 days activity distribution by type - Conditional rendering (only if activities exist) Still TODO: - History: Add type badge display (next commit) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
df01ee3de3
commit
08cead49fe
|
|
@ -3,6 +3,7 @@ import { Upload, Pencil, Trash2, Check, X, CheckCircle } from 'lucide-react'
|
||||||
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid } from 'recharts'
|
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid } from 'recharts'
|
||||||
import { api } from '../utils/api'
|
import { api } from '../utils/api'
|
||||||
import UsageBadge from '../components/UsageBadge'
|
import UsageBadge from '../components/UsageBadge'
|
||||||
|
import TrainingTypeSelect from '../components/TrainingTypeSelect'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import 'dayjs/locale/de'
|
import 'dayjs/locale/de'
|
||||||
dayjs.locale('de')
|
dayjs.locale('de')
|
||||||
|
|
@ -18,7 +19,10 @@ function empty() {
|
||||||
date: dayjs().format('YYYY-MM-DD'),
|
date: dayjs().format('YYYY-MM-DD'),
|
||||||
activity_type: 'Traditionelles Krafttraining',
|
activity_type: 'Traditionelles Krafttraining',
|
||||||
duration_min: '', kcal_active: '',
|
duration_min: '', kcal_active: '',
|
||||||
hr_avg: '', hr_max: '', rpe: '', notes: ''
|
hr_avg: '', hr_max: '', rpe: '', notes: '',
|
||||||
|
training_type_id: null,
|
||||||
|
training_category: null,
|
||||||
|
training_subcategory: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,11 +93,19 @@ function EntryForm({ form, setForm, onSave, onCancel, saveLabel='Speichern', sav
|
||||||
<input type="date" className="form-input" style={{width:140}} value={form.date} onChange={e=>set('date',e.target.value)}/>
|
<input type="date" className="form-input" style={{width:140}} value={form.date} onChange={e=>set('date',e.target.value)}/>
|
||||||
<span className="form-unit"/>
|
<span className="form-unit"/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-row">
|
<div style={{marginBottom:12}}>
|
||||||
<label className="form-label">Trainingsart</label>
|
<TrainingTypeSelect
|
||||||
<select className="form-select" value={form.activity_type} onChange={e=>set('activity_type',e.target.value)}>
|
value={form.training_type_id}
|
||||||
{ACTIVITY_TYPES.map(t=><option key={t} value={t}>{t}</option>)}
|
onChange={(typeId, category, subcategory) => {
|
||||||
</select>
|
setForm(f => ({
|
||||||
|
...f,
|
||||||
|
training_type_id: typeId,
|
||||||
|
training_category: category,
|
||||||
|
training_subcategory: subcategory
|
||||||
|
}))
|
||||||
|
}}
|
||||||
|
required={false}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
<label className="form-label">Dauer</label>
|
<label className="form-label">Dauer</label>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { useProfile } from '../context/ProfileContext'
|
||||||
import { getBfCategory } from '../utils/calc'
|
import { getBfCategory } from '../utils/calc'
|
||||||
import TrialBanner from '../components/TrialBanner'
|
import TrialBanner from '../components/TrialBanner'
|
||||||
import EmailVerificationBanner from '../components/EmailVerificationBanner'
|
import EmailVerificationBanner from '../components/EmailVerificationBanner'
|
||||||
|
import TrainingTypeDistribution from '../components/TrainingTypeDistribution'
|
||||||
import { getInterpretation, getStatusColor, getStatusBg } from '../utils/interpret'
|
import { getInterpretation, getStatusColor, getStatusBg } from '../utils/interpret'
|
||||||
import Markdown from '../utils/Markdown'
|
import Markdown from '../utils/Markdown'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|
@ -470,6 +471,20 @@ export default function Dashboard() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Training Type Distribution */}
|
||||||
|
{activities.length > 0 && (
|
||||||
|
<div className="card section-gap" style={{marginBottom:16}}>
|
||||||
|
<div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:12}}>
|
||||||
|
<div style={{fontWeight:600,fontSize:13}}>🏋️ Trainingstyp-Verteilung</div>
|
||||||
|
<button style={{background:'none',border:'none',fontSize:12,color:'var(--accent)',cursor:'pointer'}}
|
||||||
|
onClick={()=>nav('/activity')}>
|
||||||
|
Details →
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<TrainingTypeDistribution days={28} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Latest AI insight */}
|
{/* Latest AI insight */}
|
||||||
<div className="card section-gap">
|
<div className="card section-gap">
|
||||||
<div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:8}}>
|
<div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:8}}>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user