import { useState } from 'react'; import { INJURY_TRACKERS, TRACKED_GRADE_SCALE, capGradeAtMax, getGradeScore, } from '../utils/weekUtils'; import './SessionLog.css'; const SESSION_TYPES = { lead: 'Lead', bouldering: 'Bouldering', }; function getDuringPain(session, trackerKey) { return session[`${trackerKey}_during`] ?? session[`${trackerKey}_pain`] ?? ''; } function getAfterPain(session, trackerKey) { return session[`${trackerKey}_after`] ?? ''; } function SessionLog({ sessions, onEditSession, onDeleteSession }) { const [editingId, setEditingId] = useState(null); const [editForm, setEditForm] = useState({}); if (!sessions || sessions.length === 0) { return (

Session History

No climbing sessions logged yet. Add your first one above.

); } const sortedSessions = [...sessions].sort((a, b) => b.date.localeCompare(a.date)); function formatDate(dateString) { const date = new Date(`${dateString}T00:00:00`); return date.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } function handleEdit(session) { setEditingId(session.id); setEditForm({ date: session.date, session_type: session.session_type || 'bouldering', routes_count: session.routes_count, max_grade: session.max_grade || '', rpe: session.rpe || 5, left_elbow_during: getDuringPain(session, 'left_elbow'), left_elbow_after: getAfterPain(session, 'left_elbow'), right_shoulder_during: getDuringPain(session, 'right_shoulder'), right_shoulder_after: getAfterPain(session, 'right_shoulder'), notes: session.notes || '', }); } function handleSave() { const routes = parseInt(editForm.routes_count, 10); const rpe = parseInt(editForm.rpe, 10); const maxGrade = capGradeAtMax(editForm.max_grade); const maxGradeScore = getGradeScore(maxGrade); const leftElbowDuring = editForm.left_elbow_during === '' ? null : Number(editForm.left_elbow_during); const leftElbowAfter = editForm.left_elbow_after === '' ? null : Number(editForm.left_elbow_after); const rightShoulderDuring = editForm.right_shoulder_during === '' ? null : Number(editForm.right_shoulder_during); const rightShoulderAfter = editForm.right_shoulder_after === '' ? null : Number(editForm.right_shoulder_after); if ( !editForm.date || Number.isNaN(routes) || routes <= 0 || Number.isNaN(rpe) || rpe < 1 || rpe > 10 || !maxGrade || maxGradeScore == null || (leftElbowDuring != null && (Number.isNaN(leftElbowDuring) || leftElbowDuring < 0 || leftElbowDuring > 10)) || (leftElbowAfter != null && (Number.isNaN(leftElbowAfter) || leftElbowAfter < 0 || leftElbowAfter > 10)) || (rightShoulderDuring != null && (Number.isNaN(rightShoulderDuring) || rightShoulderDuring < 0 || rightShoulderDuring > 10)) || (rightShoulderAfter != null && (Number.isNaN(rightShoulderAfter) || rightShoulderAfter < 0 || rightShoulderAfter > 10)) ) { return; } const sessionType = SESSION_TYPES[editForm.session_type] ? editForm.session_type : 'bouldering'; onEditSession(editingId, { date: editForm.date, session_type: sessionType, routes_count: routes, max_grade: maxGrade, rpe, left_elbow_during: leftElbowDuring, left_elbow_after: leftElbowAfter, right_shoulder_during: rightShoulderDuring, right_shoulder_after: rightShoulderAfter, // Clear legacy single-value fields after editing. left_elbow_pain: null, right_shoulder_pain: null, notes: (editForm.notes || '').trim(), }); setEditingId(null); } function handleCancel() { setEditingId(null); } function handleDelete(id) { if (window.confirm('Delete this climbing session?')) { onDeleteSession(id); } } function handleKeyDown(event) { if (event.key === 'Enter') handleSave(); if (event.key === 'Escape') handleCancel(); } function formatSessionType(type) { return SESSION_TYPES[type] || '—'; } return (

Session History

{sortedSessions.map((session) => { if (editingId === session.id) { const previewRoutes = parseInt(editForm.routes_count, 10) || 0; const previewRpe = parseInt(editForm.rpe, 10) || 0; return ( ); } return ( ); })}
Date Type Routes Max Grade RPE Load Injury (D/A) Notes
setEditForm({ ...editForm, date: event.target.value })} onKeyDown={handleKeyDown} /> setEditForm({ ...editForm, routes_count: event.target.value })} onKeyDown={handleKeyDown} /> setEditForm({ ...editForm, max_grade: event.target.value })} onBlur={() => setEditForm((prev) => ({ ...prev, max_grade: capGradeAtMax(prev.max_grade) }))} list={`grade-scale-list-${session.id}`} onKeyDown={handleKeyDown} placeholder="e.g. 6B+ (max 7B)" /> {TRACKED_GRADE_SCALE.map((grade) => ( setEditForm({ ...editForm, rpe: event.target.value })} onKeyDown={handleKeyDown} /> {(previewRoutes * previewRpe).toFixed(0)}
{INJURY_TRACKERS.map((tracker) => ( ))}
setEditForm({ ...editForm, notes: event.target.value })} onKeyDown={handleKeyDown} placeholder="Notes..." />
{formatDate(session.date)} {formatSessionType(session.session_type)} {session.routes_count} {session.max_grade || '—'} {session.rpe}/10 {(session.routes_count * session.rpe).toFixed(0)}
LE: {session.left_elbow_during ?? session.left_elbow_pain ?? '—'}/{session.left_elbow_after ?? '—'}
RS: {session.right_shoulder_during ?? session.right_shoulder_pain ?? '—'}/{session.right_shoulder_after ?? '—'}
{session.notes || ''}
); } export default SessionLog;