Spaces:
Running
Running
| import './WeeklySummary.css'; | |
| function WeeklySummary({ summary, recommendation, weekKey, isCurrentWeek, weeksOfData, longestRun, hasPrevWeek, hasNextWeek, onPrevWeek, onNextWeek }) { | |
| const hasRuns = summary.run_count > 0; | |
| return ( | |
| <div className="weekly-summary card"> | |
| <div className="week-nav"> | |
| <button className="week-nav-btn" disabled={!hasPrevWeek} onClick={onPrevWeek} aria-label="Previous week">‹</button> | |
| <h2>{isCurrentWeek ? 'This Week' : 'Week'} <span className="week-label">{weekKey}</span></h2> | |
| <button className="week-nav-btn" disabled={!hasNextWeek} onClick={onNextWeek} aria-label="Next week">›</button> | |
| </div> | |
| {hasRuns ? ( | |
| <> | |
| <div className="stats-grid"> | |
| <div className="stat-card"> | |
| <span className="stat-value">{summary.total_distance_km.toFixed(1)}</span> | |
| <span className="stat-label">km total</span> | |
| </div> | |
| <div className="stat-card"> | |
| <span className="stat-value">{summary.total_training_load.toFixed(0)}</span> | |
| <span className="stat-label">training load</span> | |
| </div> | |
| <div className="stat-card"> | |
| <span className="stat-value">{summary.run_count}</span> | |
| <span className="stat-label">runs</span> | |
| </div> | |
| <div className="stat-card"> | |
| <span className="stat-value">{summary.avg_pace.toFixed(1)}</span> | |
| <span className="stat-label">min/km avg</span> | |
| </div> | |
| </div> | |
| {longestRun && ( | |
| <div className="longest-run"> | |
| <h3> | |
| Single Run Cap <span className="rec-method">(BJSM 30-day)</span> | |
| <span className="info-tip-wrapper"> | |
| <span className="info-icon">β</span> | |
| <span className="info-tooltip"> | |
| Overuse injury risk increases when a single session exceeds 10% of the longest run in the past 30 days. | |
| <a href="https://bjsm.bmj.com/content/59/17/1203" target="_blank" rel="noopener noreferrer"> | |
| Read the paper β | |
| </a> | |
| </span> | |
| </span> | |
| </h3> | |
| <div className="rec-row"> | |
| <span className="rec-label">Longest run (30d)</span> | |
| <span className="rec-value">{longestRun.longest_km} km</span> | |
| </div> | |
| <div className="rec-row"> | |
| <span className="rec-label">Max next run (+10%)</span> | |
| <span className="rec-value threshold-value">{longestRun.threshold_km} km</span> | |
| </div> | |
| </div> | |
| )} | |
| <div className="recommendation"> | |
| <h3> | |
| Next Week Target <span className="rec-method">(ACWR, {weeksOfData || 1}w avg)</span> | |
| <span className="info-tip-wrapper"> | |
| <span className="info-icon">β</span> | |
| <span className="info-tooltip"> | |
| The Acute:Chronic Workload Ratio compares your recent training to your rolling average over the last {weeksOfData || 1} week{(weeksOfData || 1) > 1 ? 's' : ''} (up to 4). A safe range is 0.8β1.3Γ chronic load. | |
| <a href="https://bjsm.bmj.com/content/50/5/273" target="_blank" rel="noopener noreferrer"> | |
| Read the paper β | |
| </a> | |
| </span> | |
| </span> | |
| </h3> | |
| <div className="rec-row"> | |
| <span className="rec-label">Distance</span> | |
| <span className="rec-value"> | |
| {recommendation.min_distance} β {recommendation.max_distance} km | |
| </span> | |
| </div> | |
| <div className="rec-row"> | |
| <span className="rec-label">Training Load</span> | |
| <span className="rec-value"> | |
| {recommendation.min_load} β {recommendation.max_load} | |
| </span> | |
| </div> | |
| </div> | |
| </> | |
| ) : ( | |
| <p className="empty-message">No runs logged this week yet. Add your first run to see stats and recommendations.</p> | |
| )} | |
| </div> | |
| ); | |
| } | |
| export default WeeklySummary; | |