89 lines
2.7 KiB
React
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
|