feat(exercise-detail): add embedded peek functionality for individual exercises
All checks were successful
Deploy Development / deploy (push) Successful in 40s
Test Suite / pytest-backend (push) Successful in 35s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / playwright-tests (push) Successful in 1m9s
Test Suite / pytest-backend (pull_request) Successful in 34s
Test Suite / lint-backend (pull_request) Successful in 0s
Test Suite / build-frontend (pull_request) Successful in 11s
Test Suite / playwright-tests (pull_request) Successful in 55s

- Introduced `ExercisePeekModal` to allow users to view details of individual exercises without navigating away from the Exercise Detail Page.
- Updated the UI to include a button interaction for peeking at exercises, enhancing user experience and accessibility.
- Modified the description in the Exercise Detail section to clarify the new peek feature and its functionality.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lars 2026-05-13 21:58:24 +02:00
parent 502dddd3b3
commit 85163ad440

View File

@ -4,6 +4,7 @@ import api from '../utils/api'
import ExerciseRichTextBlock from '../components/ExerciseRichTextBlock'
import ExerciseAttachmentMediaStrip from '../components/ExerciseAttachmentMediaStrip'
import CombinationPlanBracket from '../components/CombinationPlanBracket'
import ExercisePeekModal from '../components/ExercisePeekModal'
import { formatSkillLevelSlug } from '../constants/skillLevels'
function TagRow({ exercise }) {
@ -58,6 +59,8 @@ function ExerciseDetailPage() {
const [exercise, setExercise] = useState(null)
const [error, setError] = useState(null)
const [loading, setLoading] = useState(true)
/** Schnellansicht für eingebettete Einzelübungen (Kombination) — ohne Route zu verlassen */
const [embeddedPeekExerciseId, setEmbeddedPeekExerciseId] = useState(null)
useEffect(() => {
let cancelled = false
@ -121,6 +124,12 @@ function ExerciseDetailPage() {
return (
<div className="exercise-detail-shell" style={{ padding: '12px 12px 24px' }}>
<ExercisePeekModal
key={embeddedPeekExerciseId != null ? String(embeddedPeekExerciseId) : 'exercise-detail-peek'}
open={embeddedPeekExerciseId != null}
exerciseId={embeddedPeekExerciseId}
onClose={() => setEmbeddedPeekExerciseId(null)}
/>
<div style={{ marginBottom: '12px', display: 'flex', justifyContent: 'space-between', gap: '8px', flexWrap: 'wrap', alignItems: 'center' }}>
<button type="button" className="btn btn-secondary" onClick={() => navigate('/exercises')}>
Übersicht
@ -152,7 +161,9 @@ function ExerciseDetailPage() {
<section className="card exercise-detail-section">
<h2>Ablauf und Stationen</h2>
<p style={{ marginTop: 0, fontSize: '0.88rem', color: 'var(--text2)', lineHeight: 1.45 }}>
KatalogAblauf mit Archetyp, Zeiten und Stationen dieselbe Darstellung wie in der Planung und Vorschau.
KatalogAblauf mit Archetyp, Zeiten und Stationen. Station bzw. Einzelübung antippen öffnet eine
Schnellansicht mit Kurztext und Ablauf, ohne diese Seite zu verlassen. Die vollständige Übungsseite
liegt im Popup unten als Link.
</p>
<div className="training-run-combo-embed">
<CombinationPlanBracket
@ -160,7 +171,8 @@ function ExerciseDetailPage() {
methodProfile={catalogMethodProfileForBracket}
combinationSlots={exercise.combination_slots}
planningAdjusted={false}
candidateInteraction="link"
candidateInteraction="button"
onCandidatePeek={(exerciseId) => setEmbeddedPeekExerciseId(Number(exerciseId))}
/>
</div>
</section>