3fefc550fe
- Django backend with DRF (clubs, wrestlers, trainers, exercises, templates, trainings, homework, locations, leistungstest) - Next.js 16 frontend with React, Shadcn UI, Tailwind - JWT authentication - Full CRUD for all entities - Calendar view for trainings - Homework management system - Leistungstest tracking
5.8 KiB
5.8 KiB
Leistungstest Design
Date: 2026-03-23
Status: Approved
Overview
Create a "Leistungstest" (Performance Test) page for creating fitness test templates and assigning them to wrestlers. Each test records exercise results, total time, and rating. Results are tracked over time with progress visualization and leaderboards.
Design
Layout
Single page with 4 tabs:
- Vorlagen (📋) — Create/edit/delete test templates
- Zuweisen (📝) — Assign template to wrestler, record results
- Ergebnisse (📊) — View results with progress tracking
- Leaderboard (🏆) — Rankings by template
Plus: Sidebar navigation item "Leistungstest"
Tab 1: Vorlagen
Template List:
- Card for each template showing name, exercise list, usage count
- Delete button on each card
Create Template Form:
- Name input
- Dynamic list of exercises with target reps
- Add/remove exercise buttons
- Save button
Tab 2: Zuweisen
Selection:
- Wrestler dropdown (shows names, not IDs)
- Template dropdown (shows names, not IDs)
Test Form (when both selected):
- List of exercises from template
- For each exercise: target reps input + actual result input
- Total time input (minutes)
- Overall rating (5 stars)
- Notes textarea
- Submit button
Tab 3: Ergebnisse
Filters:
- Wrestler dropdown
- Template dropdown
Results Table:
- Columns: Date, Wrestler, Template, Score (%), Rating, Time
- Sorted by date (newest first)
Progress Section (when one wrestler + one template selected):
- Shows improvement over time for each exercise
- Progress bars with percentage change
Tab 4: Leaderboard
Selection:
- Template dropdown
Rankings Table:
- Columns: Rank, Wrestler, Score %, Rating, Time
- Sorted by score (highest first)
- Medal icons for top 3 (🥇🥈🥉)
Data Models
Backend Model: LeistungstestTemplate
class LeistungstestTemplate(models.Model):
name = CharField(max_length=200)
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
Backend Model: LeistungstestTemplateExercise
class LeistungstestTemplateExercise(models.Model):
template = ForeignKey(LeistungstestTemplate, related_name='exercises')
exercise = ForeignKey('exercises.Exercise')
target_reps = PositiveIntegerField()
order = IntegerField(default=0)
class Meta:
ordering = ['template', 'order']
unique_together = ['template', 'exercise']
Backend Model: LeistungstestResult
class LeistungstestResult(models.Model):
template = ForeignKey(LeistungstestTemplate)
wrestler = ForeignKey('wrestlers.Wrestler')
total_time_minutes = PositiveIntegerField(null=True, blank=True)
rating = PositiveSmallIntegerField(choices=[(1,1),(2,2),(3,3),(4,4),(5,5)], default=3)
notes = TextField(blank=True)
completed_at = DateTimeField(default=timezone.now)
created_at = DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-completed_at']
indexes = [
Index(fields=['wrestler']),
Index(fields=['template']),
Index(fields=['completed_at']),
]
Backend Model: LeistungstestResultItem
class LeistungstestResultItem(models.Model):
result = ForeignKey(LeistungstestResult, related_name='items')
exercise = ForeignKey('exercises.Exercise')
target_reps = PositiveIntegerField()
actual_reps = PositiveIntegerField()
order = IntegerField(default=0)
class Meta:
ordering = ['result', 'order']
API Endpoints
# Templates
GET /api/v1/leistungstest/templates/ — List templates
POST /api/v1/leistungstest/templates/ — Create template
GET /api/v1/leistungstest/templates/{id}/ — Get template
PATCH /api/v1/leistungstest/templates/{id}/ — Update template
DELETE /api/v1/leistungstest/templates/{id}/ — Delete template
# Template Exercises
POST /api/v1/leistungstest/template-exercises/ — Add exercise to template
DELETE /api/v1/leistungstest/template-exercises/{id}/ — Remove exercise
# Results
GET /api/v1/leistungstest/results/ — List results (filterable)
POST /api/v1/leistungstest/results/ — Create result
GET /api/v1/leistungstest/results/{id}/ — Get result
DELETE /api/v1/leistungstest/results/{id}/ — Delete result
# Leaderboard
GET /api/v1/leistungstest/leaderboard/ — Get rankings by template
Response Shapes
Template Response
{
"id": 1,
"name": "Kraft-Test",
"exercises": [
{"id": 1, "exercise": 1, "exercise_name": "Klimmzüge", "target_reps": 20, "order": 0},
{"id": 2, "exercise": 2, "exercise_name": "Liegestütze", "target_reps": 50, "order": 1}
],
"usage_count": 12,
"created_at": "2026-03-20T10:00:00Z"
}
Create Result Request
{
"template": 1,
"wrestler": 1,
"total_time_minutes": 12,
"rating": 4,
"notes": "Gute Leistung",
"items": [
{"exercise": 1, "target_reps": 20, "actual_reps": 20},
{"exercise": 2, "target_reps": 50, "actual_reps": 48}
]
}
Leaderboard Response
{
"template": {"id": 1, "name": "Kraft-Test"},
"rankings": [
{"rank": 1, "wrestler": {"id": 2, "name": "Anna S."}, "score_percent": 100, "rating": 5, "time_minutes": 10},
{"rank": 2, "wrestler": {"id": 1, "name": "Max M."}, "score_percent": 96, "rating": 4, "time_minutes": 12}
]
}
Implementation Notes
- Wrestler and template dropdowns show names, not IDs (use SelectValue with find)
- Score = (sum of actual_reps / sum of target_reps) * 100
- Results table shows score as percentage with progress bar
- Leaderboard only shows wrestlers who have done the specific template
- Progress tracking shows change in score between first and latest result for same wrestler+template