climbing-dashboard / src /components /WeeklySummary.js
lewtun's picture
lewtun HF Staff
Refine climbing dashboard training insights and theme
69a82ab
import './WeeklySummary.css';
const SESSION_CAP_LABELS = {
lead: 'Lead',
bouldering: 'Bouldering',
};
function WeeklySummary({
summary,
recommendation,
weekKey,
isCurrentWeek,
weeksOfData,
maxRoutes,
hasPrevWeek,
hasNextWeek,
onPrevWeek,
onNextWeek,
}) {
const hasSessions = summary.session_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">
&lsaquo;
</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">
&rsaquo;
</button>
</div>
{hasSessions ? (
<>
<div className="stats-grid">
<div className="stat-card">
<span className="stat-value">{summary.total_routes}</span>
<span className="stat-label">routes</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.session_count}</span>
<span className="stat-label">sessions</span>
</div>
<div className="stat-card">
<span className="stat-value">{summary.avg_rpe.toFixed(1)}</span>
<span className="stat-label">avg RPE</span>
</div>
</div>
{maxRoutes && (
<div className="max-routes-block">
<h3>
Single Session Cap <span className="rec-method">(30-day max +10%)</span>
</h3>
{Object.entries(maxRoutes).map(([sessionType, cap]) => (
<div key={sessionType} className="session-cap-card">
<div className="rec-row">
<span className="rec-label">{SESSION_CAP_LABELS[sessionType] || sessionType}</span>
<span className="rec-value">{cap.max_routes} max</span>
</div>
<div className="rec-row">
<span className="rec-label">Next-session guide</span>
<span className="rec-value threshold-value">{cap.threshold_routes} routes</span>
</div>
</div>
))}
</div>
)}
<div className="recommendation">
<h3>
Next Week Target <span className="rec-method">(ACWR, {weeksOfData || 1}w avg)</span>
</h3>
{Object.entries(recommendation.routes_by_type || {}).map(([sessionType, routeTarget]) => (
<div key={sessionType} className="session-cap-card">
<div className="rec-row">
<span className="rec-label">{SESSION_CAP_LABELS[sessionType] || sessionType}</span>
<span className="rec-value">
{routeTarget.min_routes} - {routeTarget.max_routes} routes
</span>
</div>
</div>
))}
{(!recommendation.routes_by_type || Object.keys(recommendation.routes_by_type).length === 0) && (
<div className="rec-row">
<span className="rec-label">Routes</span>
<span className="rec-value">
{recommendation.min_routes} - {recommendation.max_routes}
</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 sessions logged for this week yet. Add one to see your stats.</p>
)}
</div>
);
}
export default WeeklySummary;