fix: move TrainingTypeDistribution to History + improve admin form UX
All checks were successful
Deploy Development / deploy (push) Successful in 43s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s

UX improvements based on user feedback:

1. Move TrainingTypeDistribution from ActivityPage to History page
   - ActivityPage is for data entry, not visualization
   - History (Verlauf) shows personal development/progress
   - Chart now respects period selector (7/30/90/365 days)

2. Improve AdminTrainingTypesPage form styling
   - All input fields now full width (100%)
   - Labels changed from inline to headings above fields
   - Textareas increased from 2 to 4 rows
   - Added resize: vertical for textareas
   - Increased gap between fields from 12px to 16px
   - Follows style guide conventions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lars 2026-03-21 16:56:35 +01:00
parent eecc00e824
commit 967d92025c
3 changed files with 40 additions and 35 deletions

View File

@ -5,7 +5,6 @@ import { api } from '../utils/api'
import UsageBadge from '../components/UsageBadge' import UsageBadge from '../components/UsageBadge'
import TrainingTypeSelect from '../components/TrainingTypeSelect' import TrainingTypeSelect from '../components/TrainingTypeSelect'
import BulkCategorize from '../components/BulkCategorize' import BulkCategorize from '../components/BulkCategorize'
import TrainingTypeDistribution from '../components/TrainingTypeDistribution'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import 'dayjs/locale/de' import 'dayjs/locale/de'
dayjs.locale('de') dayjs.locale('de')
@ -303,11 +302,6 @@ export default function ActivityPage() {
{tab==='stats' && stats && ( {tab==='stats' && stats && (
<div> <div>
<div className="card section-gap">
<div className="card-title">🏋 Trainingstyp-Verteilung (30 Tage)</div>
<TrainingTypeDistribution days={30} />
</div>
{chartData.length>=2 && ( {chartData.length>=2 && (
<div className="card section-gap"> <div className="card section-gap">
<div className="card-title">Aktive Kalorien pro Tag</div> <div className="card-title">Aktive Kalorien pro Tag</div>

View File

@ -169,13 +169,14 @@ export default function AdminTrainingTypesPage() {
{editingId === 'new' ? ' Neuer Trainingstyp' : '✏️ Trainingstyp bearbeiten'} {editingId === 'new' ? ' Neuer Trainingstyp' : '✏️ Trainingstyp bearbeiten'}
</div> </div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}> <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
<div> <div>
<label className="form-label">Kategorie *</label> <div className="form-label">Kategorie *</div>
<select <select
className="form-input" className="form-input"
value={formData.category} value={formData.category}
onChange={e => setFormData({ ...formData, category: e.target.value })} onChange={e => setFormData({ ...formData, category: e.target.value })}
style={{ width: '100%' }}
> >
{Object.keys(categories).map(cat => ( {Object.keys(categories).map(cat => (
<option key={cat} value={cat}> <option key={cat} value={cat}>
@ -186,58 +187,61 @@ export default function AdminTrainingTypesPage() {
</div> </div>
<div> <div>
<label className="form-label">Subkategorie</label> <div className="form-label">Subkategorie</div>
<input <input
className="form-input" className="form-input"
value={formData.subcategory} value={formData.subcategory}
onChange={e => setFormData({ ...formData, subcategory: e.target.value })} onChange={e => setFormData({ ...formData, subcategory: e.target.value })}
placeholder="z.B. running, hypertrophy, meditation" placeholder="z.B. running, hypertrophy, meditation"
style={{ width: '100%' }}
/> />
<div style={{ fontSize: 11, color: 'var(--text3)', marginTop: 4 }}> <div style={{ fontSize: 11, color: 'var(--text3)', marginTop: 4 }}>
Kleingeschrieben, ohne Leerzeichen, eindeutig Kleingeschrieben, ohne Leerzeichen, eindeutig
</div> </div>
</div> </div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
<div> <div>
<label className="form-label">Name (Deutsch) *</label> <div className="form-label">Name (Deutsch) *</div>
<input <input
className="form-input" className="form-input"
value={formData.name_de} value={formData.name_de}
onChange={e => setFormData({ ...formData, name_de: e.target.value })} onChange={e => setFormData({ ...formData, name_de: e.target.value })}
placeholder="z.B. Laufen" placeholder="z.B. Laufen"
style={{ width: '100%' }}
/> />
</div> </div>
<div> <div>
<label className="form-label">Name (English) *</label> <div className="form-label">Name (English) *</div>
<input <input
className="form-input" className="form-input"
value={formData.name_en} value={formData.name_en}
onChange={e => setFormData({ ...formData, name_en: e.target.value })} onChange={e => setFormData({ ...formData, name_en: e.target.value })}
placeholder="e.g. Running" placeholder="e.g. Running"
style={{ width: '100%' }}
/> />
</div> </div>
</div>
<div> <div>
<label className="form-label">Icon (Emoji)</label> <div className="form-label">Icon (Emoji)</div>
<input <input
className="form-input" className="form-input"
value={formData.icon} value={formData.icon}
onChange={e => setFormData({ ...formData, icon: e.target.value })} onChange={e => setFormData({ ...formData, icon: e.target.value })}
placeholder="🏃" placeholder="🏃"
maxLength={10} maxLength={10}
style={{ width: '100%' }}
/> />
</div> </div>
<div> <div>
<label className="form-label">Sortierung</label> <div className="form-label">Sortierung</div>
<input <input
type="number" type="number"
className="form-input" className="form-input"
value={formData.sort_order} value={formData.sort_order}
onChange={e => setFormData({ ...formData, sort_order: parseInt(e.target.value) })} onChange={e => setFormData({ ...formData, sort_order: parseInt(e.target.value) })}
style={{ width: '100%' }}
/> />
<div style={{ fontSize: 11, color: 'var(--text3)', marginTop: 4 }}> <div style={{ fontSize: 11, color: 'var(--text3)', marginTop: 4 }}>
Niedrigere Zahlen werden zuerst angezeigt Niedrigere Zahlen werden zuerst angezeigt
@ -245,24 +249,26 @@ export default function AdminTrainingTypesPage() {
</div> </div>
<div> <div>
<label className="form-label">Beschreibung (Deutsch)</label> <div className="form-label">Beschreibung (Deutsch)</div>
<textarea <textarea
className="form-input" className="form-input"
value={formData.description_de} value={formData.description_de}
onChange={e => setFormData({ ...formData, description_de: e.target.value })} onChange={e => setFormData({ ...formData, description_de: e.target.value })}
placeholder="Optional: Beschreibung für KI-Analyse" placeholder="Optional: Beschreibung für KI-Analyse"
rows={2} rows={4}
style={{ width: '100%', resize: 'vertical' }}
/> />
</div> </div>
<div> <div>
<label className="form-label">Beschreibung (English)</label> <div className="form-label">Beschreibung (English)</div>
<textarea <textarea
className="form-input" className="form-input"
value={formData.description_en} value={formData.description_en}
onChange={e => setFormData({ ...formData, description_en: e.target.value })} onChange={e => setFormData({ ...formData, description_en: e.target.value })}
placeholder="Optional: Description for AI analysis" placeholder="Optional: Description for AI analysis"
rows={2} rows={4}
style={{ width: '100%', resize: 'vertical' }}
/> />
</div> </div>

View File

@ -10,6 +10,7 @@ import { api } from '../utils/api'
import { getBfCategory } from '../utils/calc' import { getBfCategory } from '../utils/calc'
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 TrainingTypeDistribution from '../components/TrainingTypeDistribution'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import 'dayjs/locale/de' import 'dayjs/locale/de'
dayjs.locale('de') dayjs.locale('de')
@ -653,6 +654,10 @@ function ActivitySection({ activities, insights, onRequest, loadingSlug, filterA
</div> </div>
))} ))}
</div> </div>
<div className="card" style={{marginBottom:12}}>
<div style={{fontSize:12,fontWeight:600,color:'var(--text3)',marginBottom:8}}>Trainingstyp-Verteilung</div>
<TrainingTypeDistribution days={period === 9999 ? 365 : period} />
</div>
<div style={{marginBottom:12}}> <div style={{marginBottom:12}}>
<div style={{fontSize:12,fontWeight:600,color:'var(--text3)',marginBottom:8}}>BEWERTUNG</div> <div style={{fontSize:12,fontWeight:600,color:'var(--text3)',marginBottom:8}}>BEWERTUNG</div>
{actRules.map((item,i)=><RuleCard key={i} item={item}/>)} {actRules.map((item,i)=><RuleCard key={i} item={item}/>)}