Files
gravl/frontend/src/components/exercises/ExerciseCard.jsx
T

89 lines
2.7 KiB
React

import './exerciseRecommendations.css'
const difficultyTokens = {
easy: { label: 'Easy', className: 'difficulty-easy' },
medium: { label: 'Medium', className: 'difficulty-medium' },
med: { label: 'Medium', className: 'difficulty-medium' },
hard: { label: 'Hard', className: 'difficulty-hard' }
}
const normalizeDifficulty = (difficulty) => {
if (!difficulty) return null
const key = String(difficulty).trim().toLowerCase()
return difficultyTokens[key] || { label: difficulty, className: 'difficulty-custom' }
}
const formatDuration = (exercise) => {
const value = exercise?.duration ?? exercise?.duration_min ?? exercise?.durationMinutes
if (!value) return null
return `${value} min`
}
const formatReps = (exercise) => {
const { reps, reps_min, reps_max, repsMin, repsMax } = exercise || {}
if (reps) return `${reps} reps`
const min = reps_min ?? repsMin
const max = reps_max ?? repsMax
if (min && max) return `${min}-${max} reps`
if (min) return `${min}+ reps`
return null
}
function ExerciseCard({
exercise,
onSelect,
className = '',
compact = false,
showMeta = true
}) {
if (!exercise) return null
const difficulty = normalizeDifficulty(exercise.difficulty)
const duration = formatDuration(exercise)
const reps = formatReps(exercise)
const imageSrc = exercise.image_url || exercise.image || exercise.imageUrl
const Element = onSelect ? 'button' : 'article'
return (
<Element
type={onSelect ? 'button' : undefined}
className={`exercise-recommendation-card ${compact ? 'is-compact' : ''} ${className}`}
onClick={onSelect ? () => onSelect(exercise) : undefined}
>
<div className="exercise-card-media">
{imageSrc ? (
<img src={imageSrc} alt={exercise.name} loading="lazy" />
) : (
<div className="exercise-card-placeholder" aria-hidden="true">
<span>{exercise.name?.slice(0, 1) || 'E'}</span>
</div>
)}
</div>
<div className="exercise-card-content">
<div className="exercise-card-header">
<h3>{exercise.name}</h3>
{difficulty && (
<span className={`difficulty-badge ${difficulty.className}`}>
{difficulty.label}
</span>
)}
</div>
{exercise.description && !compact && (
<p className="exercise-card-description">{exercise.description}</p>
)}
{showMeta && (duration || reps) && (
<div className="exercise-card-meta">
{duration && <span className="exercise-meta-pill">{duration}</span>}
{reps && <span className="exercise-meta-pill">{reps}</span>}
</div>
)}
</div>
</Element>
)
}
export default ExerciseCard