workout-board / index.html
PhoenixBomb's picture
Add 2 files
010b4f7 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Workout Analytics Dashboard</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
<style>
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #888;
border-radius: 10px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #555;
}
.chart-container {
position: relative;
height: 100%;
width: 100%;
}
.progress-ring__circle {
transition: stroke-dashoffset 0.5s;
transform: rotate(-90deg);
transform-origin: 50% 50%;
}
.blink {
animation: blink-animation 1.5s steps(5, start) infinite;
}
@keyframes blink-animation {
to {
visibility: hidden;
}
}
</style>
</head>
<body class="bg-gray-100 font-sans">
<div class="container mx-auto px-4 py-8">
<div class="flex justify-between items-center mb-8">
<h1 class="text-3xl font-bold text-gray-800">Workout Performance Analytics</h1>
<div class="flex space-x-4">
<div class="relative">
<select class="appearance-none bg-white border border-gray-300 rounded-md py-2 pl-3 pr-8 text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500">
<option>Last 7 Days</option>
<option selected>Last 30 Days</option>
<option>Last 90 Days</option>
<option>Custom Range</option>
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
<i class="fas fa-chevron-down"></i>
</div>
</div>
<button class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md flex items-center">
<i class="fas fa-download mr-2"></i> Export
</button>
</div>
</div>
<!-- Summary Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<div class="bg-white rounded-xl shadow-md p-6 flex items-center">
<div class="p-3 rounded-full bg-blue-100 text-blue-600 mr-4">
<i class="fas fa-dumbbell text-xl"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Total Volume</p>
<h3 class="text-2xl font-bold">12,450 kg</h3>
<p class="text-green-500 text-sm flex items-center">
<i class="fas fa-arrow-up mr-1"></i> 8.2% from last month
</p>
</div>
</div>
<div class="bg-white rounded-xl shadow-md p-6 flex items-center">
<div class="p-3 rounded-full bg-purple-100 text-purple-600 mr-4">
<i class="fas fa-clock text-xl"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Avg Rest Time</p>
<h3 class="text-2xl font-bold">72s</h3>
<p class="text-red-500 text-sm flex items-center">
<i class="fas fa-arrow-down mr-1"></i> 5.3% from last month
</p>
</div>
</div>
<div class="bg-white rounded-xl shadow-md p-6 flex items-center">
<div class="p-3 rounded-full bg-green-100 text-green-600 mr-4">
<i class="fas fa-fire text-xl"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Workout Intensity</p>
<h3 class="text-2xl font-bold">78%</h3>
<p class="text-green-500 text-sm flex items-center">
<i class="fas fa-arrow-up mr-1"></i> 12.1% from last month
</p>
</div>
</div>
<div class="bg-white rounded-xl shadow-md p-6 flex items-center">
<div class="p-3 rounded-full bg-orange-100 text-orange-600 mr-4">
<i class="fas fa-heartbeat text-xl"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Avg Heart Rate</p>
<h3 class="text-2xl font-bold">142 bpm</h3>
<p class="text-green-500 text-sm flex items-center">
<i class="fas fa-arrow-up mr-1"></i> 3.7% from last month
</p>
</div>
</div>
</div>
<!-- Main Charts -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
<!-- Workout Volume & Intensity Chart -->
<div class="bg-white rounded-xl shadow-md p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold text-gray-800">Workout Volume & Intensity</h2>
<div class="flex space-x-2">
<button class="px-3 py-1 text-sm bg-blue-100 text-blue-600 rounded-md">Volume</button>
<button class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md">Intensity</button>
<button class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md">Both</button>
</div>
</div>
<div class="chart-container">
<canvas id="volumeIntensityChart"></canvas>
</div>
</div>
<!-- Rest Periods & Recovery Chart -->
<div class="bg-white rounded-xl shadow-md p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold text-gray-800">Rest Periods & Recovery</h2>
<div class="flex items-center text-sm text-gray-500">
<i class="fas fa-info-circle mr-1"></i> Optimal range: 60-90s
</div>
</div>
<div class="chart-container">
<canvas id="restRecoveryChart"></canvas>
</div>
</div>
</div>
<!-- Secondary Charts -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-8">
<!-- Muscle Group Distribution -->
<div class="bg-white rounded-xl shadow-md p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4">Muscle Group Distribution</h2>
<div class="chart-container">
<canvas id="muscleGroupChart"></canvas>
</div>
</div>
<!-- Weight Progression -->
<div class="bg-white rounded-xl shadow-md p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4">Weight Progression</h2>
<div class="chart-container">
<canvas id="weightProgressionChart"></canvas>
</div>
</div>
<!-- Workout Consistency -->
<div class="bg-white rounded-xl shadow-md p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4">Workout Consistency</h2>
<div class="flex items-center justify-center h-48">
<svg class="w-40 h-40">
<circle
class="text-gray-200"
stroke-width="10"
stroke="currentColor"
fill="transparent"
r="60"
cx="80"
cy="80"
/>
<circle
class="progress-ring__circle text-blue-600"
stroke-width="10"
stroke-linecap="round"
stroke="currentColor"
fill="transparent"
r="60"
cx="80"
cy="80"
stroke-dasharray="377"
stroke-dashoffset="75.4"
/>
</svg>
<div class="absolute text-center">
<span class="text-3xl font-bold">80%</span>
<p class="text-gray-500 text-sm">Completion Rate</p>
</div>
</div>
<div class="mt-4 text-center">
<p class="text-gray-600">You've completed 16 of 20 planned workouts this month</p>
</div>
</div>
</div>
<!-- Detailed Metrics Table -->
<div class="bg-white rounded-xl shadow-md overflow-hidden mb-8">
<div class="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
<h2 class="text-xl font-semibold text-gray-800">Workout Session Details</h2>
<div class="relative">
<input type="text" placeholder="Search workouts..." class="pl-8 pr-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
</div>
</div>
<div class="overflow-x-auto custom-scrollbar">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Workout</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Volume (kg)</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Avg Rest</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Intensity</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">HR Max</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Recovery</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">Jun 12, 2023</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-blue-600">Upper Body</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">2,450</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">68s</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<div class="flex items-center">
<div class="h-2 w-16 bg-gray-200 rounded-full mr-2">
<div class="h-2 bg-green-500 rounded-full" style="width: 75%"></div>
</div>
<span>75%</span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">158 bpm</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<span class="px-2 py-1 text-xs rounded-full bg-green-100 text-green-800">Excellent</span>
</td>
</tr>
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">Jun 10, 2023</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-blue-600">Leg Day</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">3,120</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">82s</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<div class="flex items-center">
<div class="h-2 w-16 bg-gray-200 rounded-full mr-2">
<div class="h-2 bg-yellow-500 rounded-full" style="width: 65%"></div>
</div>
<span>65%</span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">162 bpm</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<span class="px-2 py-1 text-xs rounded-full bg-yellow-100 text-yellow-800">Good</span>
</td>
</tr>
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">Jun 8, 2023</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-blue-600">Full Body</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">2,780</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">71s</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<div class="flex items-center">
<div class="h-2 w-16 bg-gray-200 rounded-full mr-2">
<div class="h-2 bg-green-500 rounded-full" style="width: 80%"></div>
</div>
<span>80%</span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">155 bpm</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<span class="px-2 py-1 text-xs rounded-full bg-green-100 text-green-800">Excellent</span>
</td>
</tr>
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">Jun 5, 2023</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-blue-600">Upper Body</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">2,310</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">65s</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<div class="flex items-center">
<div class="h-2 w-16 bg-gray-200 rounded-full mr-2">
<div class="h-2 bg-red-500 rounded-full" style="width: 45%"></div>
</div>
<span>45%</span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">148 bpm</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<span class="px-2 py-1 text-xs rounded-full bg-red-100 text-red-800">Poor</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="px-6 py-4 border-t border-gray-200 flex items-center justify-between">
<div class="text-sm text-gray-500">
Showing <span class="font-medium">1</span> to <span class="font-medium">4</span> of <span class="font-medium">16</span> workouts
</div>
<div class="flex space-x-2">
<button class="px-3 py-1 border border-gray-300 rounded-md text-sm bg-white text-gray-700 hover:bg-gray-50">Previous</button>
<button class="px-3 py-1 border border-gray-300 rounded-md text-sm bg-blue-600 text-white hover:bg-blue-700">1</button>
<button class="px-3 py-1 border border-gray-300 rounded-md text-sm bg-white text-gray-700 hover:bg-gray-50">2</button>
<button class="px-3 py-1 border border-gray-300 rounded-md text-sm bg-white text-gray-700 hover:bg-gray-50">3</button>
<button class="px-3 py-1 border border-gray-300 rounded-md text-sm bg-white text-gray-700 hover:bg-gray-50">Next</button>
</div>
</div>
</div>
<!-- Insights Section -->
<div class="bg-white rounded-xl shadow-md p-6 mb-8">
<h2 class="text-xl font-semibold text-gray-800 mb-4">Performance Insights</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="border-l-4 border-blue-500 pl-4">
<h3 class="text-lg font-medium text-gray-800 mb-2">Positive Trend</h3>
<p class="text-gray-600">Your workout volume has increased by 8.2% compared to last month, indicating progressive overload which is essential for muscle growth.</p>
</div>
<div class="border-l-4 border-yellow-500 pl-4">
<h3 class="text-lg font-medium text-gray-800 mb-2">Attention Needed</h3>
<p class="text-gray-600">Your average rest periods have decreased by 5.3%. While this may increase workout density, ensure you're getting adequate recovery between sets.</p>
</div>
<div class="border-l-4 border-green-500 pl-4">
<h3 class="text-lg font-medium text-gray-800 mb-2">Excellent Progress</h3>
<p class="text-gray-600">Your workout intensity has improved significantly (+12.1%), showing you're pushing yourself appropriately during sessions.</p>
</div>
<div class="border-l-4 border-purple-500 pl-4">
<h3 class="text-lg font-medium text-gray-800 mb-2">Recommendation</h3>
<p class="text-gray-600">Consider adding 1-2 additional lower body sessions per week to balance your muscle group distribution (currently 65% upper body focus).</p>
</div>
</div>
</div>
</div>
<script>
// Volume & Intensity Chart
const volumeIntensityCtx = document.getElementById('volumeIntensityChart').getContext('2d');
const volumeIntensityChart = new Chart(volumeIntensityCtx, {
type: 'line',
data: {
labels: ['Week 1', 'Week 2', 'Week 3', 'Week 4', 'Week 5', 'Week 6'],
datasets: [
{
label: 'Workout Volume (kg)',
data: [8500, 9200, 8800, 10200, 11000, 12450],
borderColor: 'rgba(59, 130, 246, 1)',
backgroundColor: 'rgba(59, 130, 246, 0.05)',
borderWidth: 2,
fill: true,
tension: 0.3,
yAxisID: 'y'
},
{
label: 'Workout Intensity (%)',
data: [65, 68, 70, 72, 75, 78],
borderColor: 'rgba(16, 185, 129, 1)',
backgroundColor: 'rgba(16, 185, 129, 0.05)',
borderWidth: 2,
borderDash: [5, 5],
tension: 0.3,
yAxisID: 'y1'
}
]
},
options: {
responsive: true,
interaction: {
mode: 'index',
intersect: false,
},
plugins: {
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.datasetIndex === 0) {
label += context.parsed.y.toLocaleString() + ' kg';
} else {
label += context.parsed.y + '%';
}
return label;
}
}
},
legend: {
position: 'top',
},
annotation: {
annotations: {
line1: {
type: 'line',
yMin: 70,
yMax: 70,
borderColor: 'rgba(16, 185, 129, 0.5)',
borderWidth: 1,
borderDash: [6, 6],
label: {
content: 'Target Intensity',
enabled: true,
position: 'right'
}
}
}
}
},
scales: {
y: {
type: 'linear',
display: true,
position: 'left',
title: {
display: true,
text: 'Volume (kg)'
},
grid: {
drawOnChartArea: false
}
},
y1: {
type: 'linear',
display: true,
position: 'right',
title: {
display: true,
text: 'Intensity (%)'
},
min: 50,
max: 100,
grid: {
drawOnChartArea: false
}
}
}
}
});
// Rest & Recovery Chart
const restRecoveryCtx = document.getElementById('restRecoveryChart').getContext('2d');
const restRecoveryChart = new Chart(restRecoveryCtx, {
type: 'bar',
data: {
labels: ['Jun 5', 'Jun 8', 'Jun 10', 'Jun 12', 'Jun 15', 'Jun 18', 'Jun 22', 'Jun 25', 'Jun 28'],
datasets: [
{
label: 'Rest Period (s)',
data: [65, 71, 82, 68, 75, 70, 67, 73, 69],
backgroundColor: 'rgba(124, 58, 237, 0.7)',
borderColor: 'rgba(124, 58, 237, 1)',
borderWidth: 1,
yAxisID: 'y'
},
{
label: 'Recovery Score',
data: [62, 85, 72, 88, 78, 82, 75, 80, 84],
type: 'line',
borderColor: 'rgba(245, 158, 11, 1)',
backgroundColor: 'rgba(245, 158, 11, 0.1)',
borderWidth: 2,
pointRadius: 4,
pointBackgroundColor: 'rgba(245, 158, 11, 1)',
tension: 0.3,
yAxisID: 'y1'
}
]
},
options: {
responsive: true,
plugins: {
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.datasetIndex === 0) {
label += context.parsed.y + 's';
} else {
label += context.parsed.y + '/100';
}
return label;
}
}
},
legend: {
position: 'top',
},
annotation: {
annotations: {
box1: {
type: 'box',
yMin: 60,
yMax: 90,
backgroundColor: 'rgba(124, 58, 237, 0.05)',
borderColor: 'rgba(124, 58, 237, 0.3)',
borderWidth: 1,
label: {
content: 'Optimal Rest Range',
enabled: true,
position: 'top'
}
}
}
}
},
scales: {
y: {
type: 'linear',
display: true,
position: 'left',
title: {
display: true,
text: 'Rest Period (seconds)'
},
min: 50,
max: 100
},
y1: {
type: 'linear',
display: true,
position: 'right',
title: {
display: true,
text: 'Recovery Score'
},
min: 50,
max: 100,
grid: {
drawOnChartArea: false
}
}
}
}
});
// Muscle Group Chart
const muscleGroupCtx = document.getElementById('muscleGroupChart').getContext('2d');
const muscleGroupChart = new Chart(muscleGroupCtx, {
type: 'doughnut',
data: {
labels: ['Chest', 'Back', 'Legs', 'Shoulders', 'Arms', 'Core'],
datasets: [{
data: [25, 20, 15, 15, 15, 10],
backgroundColor: [
'rgba(59, 130, 246, 0.8)',
'rgba(16, 185, 129, 0.8)',
'rgba(245, 158, 11, 0.8)',
'rgba(124, 58, 237, 0.8)',
'rgba(239, 68, 68, 0.8)',
'rgba(14, 165, 233, 0.8)'
],
borderColor: [
'rgba(59, 130, 246, 1)',
'rgba(16, 185, 129, 1)',
'rgba(245, 158, 11, 1)',
'rgba(124, 58, 237, 1)',
'rgba(239, 68, 68, 1)',
'rgba(14, 165, 233, 1)'
],
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'right',
},
tooltip: {
callbacks: {
label: function(context) {
const label = context.label || '';
const value = context.raw || 0;
const total = context.dataset.data.reduce((a, b) => a + b, 0);
const percentage = Math.round((value / total) * 100);
return `${label}: ${percentage}% (${value} sessions)`;
}
}
}
},
cutout: '65%'
}
});
// Weight Progression Chart
const weightProgressionCtx = document.getElementById('weightProgressionChart').getContext('2d');
const weightProgressionChart = new Chart(weightProgressionCtx, {
type: 'line',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [
{
label: 'Bench Press (kg)',
data: [60, 62, 65, 67, 70, 72],
borderColor: 'rgba(59, 130, 246, 1)',
backgroundColor: 'rgba(59, 130, 246, 0.1)',
borderWidth: 2,
tension: 0.3,
fill: true
},
{
label: 'Squat (kg)',
data: [80, 82, 85, 87, 90, 95],
borderColor: 'rgba(16, 185, 129, 1)',
backgroundColor: 'rgba(16, 185, 129, 0.1)',
borderWidth: 2,
tension: 0.3,
fill: true
},
{
label: 'Deadlift (kg)',
data: [90, 92, 95, 100, 105, 110],
borderColor: 'rgba(245, 158, 11, 1)',
backgroundColor: 'rgba(245, 158, 11, 0.1)',
borderWidth: 2,
tension: 0.3,
fill: true
}
]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'top',
},
tooltip: {
callbacks: {
label: function(context) {
return context.dataset.label + ': ' + context.parsed.y + 'kg';
}
}
}
},
scales: {
y: {
beginAtZero: false,
min: 50,
title: {
display: true,
text: 'Weight (kg)'
}
}
}
}
});
// Animate progress ring
document.addEventListener('DOMContentLoaded', function() {
const circle = document.querySelector('.progress-ring__circle');
const radius = circle.r.baseVal.value;
const circumference = 2 * Math.PI * radius;
circle.style.strokeDasharray = circumference;
circle.style.strokeDashoffset = circumference - (0.8 * circumference);
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=PhoenixBomb/workout-board" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>