training-load-dataviz / index.html
glutamatt's picture
glutamatt HF Staff
show big current value
58e063c verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Training Load</title>
<link rel="icon" type="image/webp" href="/icon.webp">
<link rel="stylesheet" href="/src/style.css">
</head>
<body>
<div id="app">
<header>
<div class="header-title">
<img src="/icon.webp" alt="Training Load Icon" class="header-icon" />
<h1>Training Load</h1>
</div>
<div class="header-controls">
<div class="ftp-input-container">
<label for="ftp-input">FTP</label>
<input type="number" id="ftp-input" value="343" min="0" step="1" placeholder="343" />
<span class="ftp-unit">W</span>
</div>
<div class="ftp-input-container">
<label for="target-acwr-input">Target ACWR</label>
<input type="number" id="target-acwr-input" value="1.2" min="0.5" max="3" step="0.1"
placeholder="1.2" />
</div>
<div class="ftp-input-container">
<label for="predict-today-input"
style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" id="predict-today-input" style="width: auto; margin: 0;" />
<span>Predict today</span>
</label>
</div>
<div class="ftp-input-container">
<label for="threshold-hr-input">Threshold HR</label>
<input type="number" id="threshold-hr-input" value="190" min="100" max="220" step="1"
placeholder="170" />
<span class="ftp-unit">bpm</span>
</div>
<div class="ftp-input-container">
<label for="resting-hr-input">Resting HR</label>
<input type="number" id="resting-hr-input" value="53" min="30" max="100" step="1"
placeholder="50" />
<span class="ftp-unit">bpm</span>
</div>
<input type="file" id="csv-upload" accept=".csv" />
<label for="csv-upload" class="upload-label">
πŸ“ Upload CSV
</label>
<button id="help-button" class="help-button" title="Help">
<span>?</span>
</button>
</div>
</header>
<section id="filter-section" class="hidden">
<div class="filter-container" id="filter-container">
<!-- Activity type filters will be dynamically generated here -->
</div>
</section>
<main>
<div id="upload-status"></div>
<section id="charts-section" class="hidden">
<div class="chart-container">
<h2>πŸ—ΊοΈ Distance-based ACWR <span id="distance-acwr-display" class="acwr-display"></span></h2>
<div id="distance-target" class="target-info"></div>
<canvas id="distance-chart"></canvas>
</div>
<div class="chart-container">
<h2>⏱️ Duration-based ACWR <span id="duration-acwr-display" class="acwr-display"></span></h2>
<div id="duration-target" class="target-info"></div>
<canvas id="duration-chart"></canvas>
</div>
<div class="chart-container">
<h2>πŸ₯΅ TSS-based ACWR <span id="tss-acwr-display" class="acwr-display"></span></h2>
<div id="tss-target" class="target-info"></div>
<canvas id="tss-chart"></canvas>
</div>
<div class="chart-container">
<h2>πŸ”‹ Calories-based ACWR <span id="calories-acwr-display" class="acwr-display"></span></h2>
<div id="calories-target" class="target-info"></div>
<canvas id="calories-chart"></canvas>
</div>
<div class="legend">
<div class="legend-item">
<span class="legend-color low"></span>
<span>ACWR &lt; 0.8 - Detraining risk</span>
</div>
<div class="legend-item">
<span class="legend-color optimal"></span>
<span>ACWR 0.8 - 1.3 - Optimal</span>
</div>
<div class="legend-item">
<span class="legend-color warning"></span>
<span>ACWR 1.3 - 1.5 - Warning</span>
</div>
<div class="legend-item">
<span class="legend-color high"></span>
<span>ACWR &gt; 1.5 - Injury risk</span>
</div>
</div>
</section>
</main>
</div>
<div id="help-popover" class="help-popover hidden">
<div class="help-popover-content">
<button id="help-close" class="help-close">&times;</button>
<h2>πŸ“š Glossary</h2>
<div class="help-section">
<h3>ACWR - Acute:Chronic Workload Ratio</h3>
<p>The ratio between your recent training load (acute: last 7 days) and your longer-term training load
(chronic: last 28 days). Used to manage training progression and injury risk.</p>
<ul>
<li><strong>&lt; 0.8 (Blue):</strong> Detraining risk - you may be losing fitness</li>
<li><strong>0.8 - 1.3 (Green):</strong> Optimal zone - ideal training progression</li>
<li><strong>1.3 - 1.5 (Orange):</strong> Warning - pushing the limits</li>
<li><strong>&gt; 1.5 (Red):</strong> Injury risk - training load may be too high</li>
</ul>
</div>
<div class="help-section">
<h3>TSS - Training Stress Score</h3>
<p>A composite metric that quantifies the overall training stress of a workout, taking into account both
intensity (Normalized Power) and duration.</p>
<p><strong>Formula:</strong> TSS = (Duration Γ— NP Γ— IF) / (FTP Γ— 3600) Γ— 100</p>
</div>
<div class="help-section">
<h3>FTP - Functional Threshold Power</h3>
<p>The highest average power you can sustain for approximately one hour, measured in watts. Used as a
baseline for calculating training intensity and TSS.</p>
</div>
<div class="help-section">
<h3>NP - Normalized Power</h3>
<p>A power measurement that accounts for the variable nature of your effort during a workout, providing
a more accurate representation of the physiological cost than average power.</p>
</div>
<div class="help-section">
<h3>IF - Intensity Factor</h3>
<p>The ratio of Normalized Power to FTP (IF = NP / FTP). Indicates how hard a workout was relative to
your threshold.</p>
</div>
</div>
</div>
<div id="activity-popover" class="activity-popover hidden">
<div class="activity-popover-content">
<button id="activity-close" class="help-close">&times;</button>
<h2 id="activity-date-title"></h2>
<div id="activity-list"></div>
</div>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>