workout-complete / index.html
j1225d's picture
Can we make the "share" button save the photo to an phones images? - Follow Up Deployment
678a8a7 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kintsugi Workout - Analytics</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@media (max-width: 640px) {
.modal-enter {
width: 95%;
margin: 0 auto;
}
.grid.grid-cols-2.md\\:grid-cols-4 {
grid-template-columns: repeat(2, 1fr);
gap: 0.5rem;
}
.flex.flex-col.sm\\:flex-row {
flex-direction: column;
gap: 0.5rem;
}
button {
padding: 0.75rem 1rem;
}
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes progressGrow {
from { width: 0; }
to { width: var(--progress-value); }
}
.modal-enter {
animation: fadeIn 0.3s ease-out forwards;
}
.progress-bar {
--progress-value: 0%;
animation: progressGrow 1s ease-out forwards;
}
.gold-text {
background: linear-gradient(to right, #d4af37, #f9d423);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.gold-border {
border-color: #d4af37;
}
.gold-bg {
background: linear-gradient(135deg, rgba(212, 175, 55, 0.1), rgba(249, 212, 35, 0.1));
}
@keyframes fadeInOut {
0% { opacity: 0; transform: translateY(20px) translateX(-50%); }
10% { opacity: 1; transform: translateY(0) translateX(-50%); }
90% { opacity: 1; transform: translateY(0) translateX(-50%); }
100% { opacity: 0; transform: translateY(-20px) translateX(-50%); }
}
.fixed.bottom-4 {
animation: fadeInOut 3s ease forwards;
}
</style>
</head>
<body class="bg-gray-900 min-h-screen flex items-center justify-center p-4">
<!-- Main Content (would be hidden when modal is not active) -->
<div class="text-white text-center">
<h1 class="text-4xl font-bold mb-4 gold-text">Kintsugi Workout</h1>
<p class="mb-8">Your fitness journey, beautifully imperfect</p>
<button id="openModalBtn" class="bg-amber-600 hover:bg-amber-700 text-white font-bold py-3 px-6 rounded-full transition-all duration-300 transform hover:scale-105">
View Workout Analytics
</button>
</div>
<!-- Hidden canvas for share image -->
<canvas id="shareCanvas" class="hidden"></canvas>
<!-- Trends Modal -->
<div id="trendsModal" class="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center p-4 hidden z-50 overflow-y-auto">
<div class="bg-gray-800 rounded-xl max-w-4xl w-full max-h-[90vh] mx-4 overflow-y-auto shadow-2xl modal-enter">
<!-- Modal Header -->
<div class="gold-bg p-6 border-b gold-border">
<div class="flex justify-between items-center">
<h2 class="text-2xl font-bold gold-text">Your Workout Trends</h2>
<button id="closeTrendsModalBtn" class="text-gray-400 hover:text-white">
<i class="fas fa-times text-xl"></i>
</button>
</div>
<p class="mt-2 text-amber-200">See how far you've come!</p>
</div>
<!-- Modal Body -->
<div class="p-6">
<!-- Chart Placeholders -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<div class="bg-gray-700 rounded-lg p-4">
<h3 class="text-lg font-semibold mb-4 gold-text">Weekly Workouts</h3>
<div class="h-64 bg-gray-600 rounded flex items-center justify-center">
<p class="text-gray-400">Line chart would appear here</p>
</div>
</div>
<div class="bg-gray-700 rounded-lg p-4">
<h3 class="text-lg font-semibold mb-4 gold-text">Calories Burned</h3>
<div class="h-64 bg-gray-600 rounded flex items-center justify-center">
<p class="text-gray-400">Bar chart would appear here</p>
</div>
</div>
</div>
<!-- Progress Over Time -->
<div class="mb-6">
<h3 class="text-lg font-semibold mb-4 gold-text">Strength Progress</h3>
<div class="bg-gray-700 rounded-lg p-4">
<div class="h-64 bg-gray-600 rounded flex items-center justify-center">
<p class="text-gray-400">Progress chart would appear here</p>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="flex justify-end gap-3">
<button class="bg-amber-600 hover:bg-amber-700 text-white font-bold py-2 px-4 rounded-lg transition-all">
<i class="fas fa-download mr-2"></i> Export Data
</button>
</div>
</div>
</div>
</div>
<!-- Workout Analytics Modal -->
<div id="workoutModal" class="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center p-4 hidden overflow-y-auto">
<div class="bg-gray-800 rounded-xl max-w-2xl w-full max-h-[90vh] mx-4 overflow-y-auto shadow-2xl modal-enter">
<!-- Modal Header -->
<div class="gold-bg p-6 border-b gold-border">
<div class="flex justify-between items-center">
<h2 class="text-2xl font-bold gold-text">Workout Complete!</h2>
<button id="closeModalBtn" class="text-gray-400 hover:text-white">
<i class="fas fa-times text-xl"></i>
</button>
</div>
<p class="mt-2 text-amber-200">You're stronger than yesterday! <span class="text-white">Keep going!</span></p>
</div>
<!-- Modal Body -->
<div class="p-6">
<!-- Summary Stats -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
<div class="bg-gray-700 rounded-lg p-4 text-center">
<p class="text-gray-400 text-sm">Duration</p>
<p class="text-2xl font-bold gold-text">45:22</p>
<p class="text-xs text-amber-200">+2:18 from last time</p>
</div>
<div class="bg-gray-700 rounded-lg p-4 text-center">
<p class="text-gray-400 text-sm">Calories</p>
<p class="text-2xl font-bold gold-text">412</p>
<p class="text-xs text-amber-200">New record! 🔥</p>
</div>
<div class="bg-gray-700 rounded-lg p-4 text-center">
<p class="text-gray-400 text-sm">Exercises</p>
<p class="text-2xl font-bold gold-text">8</p>
<p class="text-xs text-amber-200">+1 new exercise</p>
</div>
<div class="bg-gray-700 rounded-lg p-4 text-center">
<p class="text-gray-400 text-sm">Intensity</p>
<div class="flex justify-center mt-1">
<i class="fas fa-fire text-amber-500 mx-1"></i>
<i class="fas fa-fire text-amber-500 mx-1"></i>
<i class="fas fa-fire text-amber-500 mx-1"></i>
<i class="fas fa-fire text-gray-600 mx-1"></i>
<i class="fas fa-fire text-gray-600 mx-1"></i>
</div>
<p class="text-xs text-amber-200 mt-1">Great effort!</p>
</div>
</div>
<!-- Progress Section -->
<div class="mb-8">
<h3 class="text-lg font-semibold mb-4 gold-text">Weekly Progress</h3>
<div class="bg-gray-700 rounded-lg p-4">
<div class="flex justify-between mb-2">
<span>Weekly Goal: 3/5 workouts</span>
<span>60%</span>
</div>
<div class="w-full bg-gray-600 rounded-full h-3">
<div class="progress-bar bg-amber-500 h-3 rounded-full" style="--progress-value: 60%"></div>
</div>
<p class="text-xs text-amber-200 mt-2">You're on track! 2 more workouts to hit your goal.</p>
</div>
</div>
<!-- Exercise Breakdown -->
<div class="mb-8">
<h3 class="text-lg font-semibold mb-4 gold-text">Exercise Breakdown</h3>
<div class="space-y-3">
<div class="flex items-center justify-between bg-gray-700 rounded-lg p-3">
<div class="flex items-center">
<div class="w-8 h-8 rounded-full gold-bg flex items-center justify-center mr-3">
<i class="fas fa-dumbbell text-amber-500"></i>
</div>
<div>
<p class="font-medium">Bench Press</p>
<p class="text-xs text-gray-400">3 sets × 10 reps</p>
</div>
</div>
<div class="text-right">
<p class="gold-text">+5 lbs</p>
<p class="text-xs text-amber-200">PR!</p>
</div>
</div>
<div class="flex items-center justify-between bg-gray-700 rounded-lg p-3">
<div class="flex items-center">
<div class="w-8 h-8 rounded-full gold-bg flex items-center justify-center mr-3">
<i class="fas fa-running text-amber-500"></i>
</div>
<div>
<p class="font-medium">Squats</p>
<p class="text-xs text-gray-400">4 sets × 8 reps</p>
</div>
</div>
<div class="text-right">
<p class="gold-text">135 lbs</p>
<p class="text-xs text-amber-200">Consistent</p>
</div>
</div>
<div class="flex items-center justify-between bg-gray-700 rounded-lg p-3">
<div class="flex items-center">
<div class="w-8 h-8 rounded-full gold-bg flex items-center justify-center mr-3">
<i class="fas fa-heartbeat text-amber-500"></i>
</div>
<div>
<p class="font-medium">HIIT</p>
<p class="text-xs text-gray-400">20 min</p>
</div>
</div>
<div class="text-right">
<p class="gold-text">+3 min</p>
<p class="text-xs text-amber-200">Longer duration</p>
</div>
</div>
</div>
</div>
<!-- Motivational Quote -->
<div class="gold-bg border-l-4 gold-border p-4 mb-6">
<p class="italic text-amber-100">"Strength doesn't come from what you can do. It comes from overcoming the things you once thought you couldn't."</p>
<p class="text-right text-sm mt-2 gold-text">- Rikki Rogers</p>
</div>
<!-- Action Buttons -->
<div class="flex flex-col sm:flex-row gap-3">
<button class="flex-1 bg-amber-600 hover:bg-amber-700 text-white font-bold py-3 px-4 rounded-lg transition-all flex items-center justify-center">
<i class="fas fa-user mr-2"></i> Profile
</button>
<button class="flex-1 bg-transparent hover:bg-gray-700 text-white font-bold py-3 px-4 rounded-lg border gold-border transition-all flex items-center justify-center">
<i class="fas fa-share-alt mr-2"></i> Share
</button>
<button class="flex-1 bg-transparent hover:bg-gray-700 text-white font-bold py-3 px-4 rounded-lg border gold-border transition-all flex items-center justify-center">
<i class="fas fa-chart-line mr-2"></i> View Trends
</button>
</div>
</div>
<!-- Modal Footer -->
<div class="gold-bg p-4 text-center border-t gold-border">
<p class="text-xs text-amber-200">Remember: Every workout makes you stronger. Your effort today is an investment in tomorrow.</p>
</div>
</div>
</div>
<script>
// Modal functionality
const openModalBtn = document.getElementById('openModalBtn');
const closeModalBtn = document.getElementById('closeModalBtn');
const workoutModal = document.getElementById('workoutModal');
openModalBtn.addEventListener('click', () => {
workoutModal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
});
closeModalBtn.addEventListener('click', () => {
workoutModal.classList.add('hidden');
document.body.style.overflow = 'auto';
});
// Close modal when clicking outside
workoutModal.addEventListener('click', (e) => {
if (e.target === workoutModal) {
workoutModal.classList.add('hidden');
document.body.style.overflow = 'auto';
}
});
// Simulate loading some data (in a real app, this would be from your backend)
setTimeout(() => {
const progressBars = document.querySelectorAll('.progress-bar');
progressBars.forEach(bar => {
bar.style.width = bar.style.getPropertyValue('--progress-value');
});
}, 100);
// Share button functionality
document.querySelector('button:has(.fa-share-alt)').addEventListener('click', () => {
const canvas = document.getElementById('shareCanvas');
const ctx = canvas.getContext('2d');
// Set canvas size (optimized for social sharing)
canvas.width = 1080;
canvas.height = 1080;
// Draw dynamic gradient background
const gradient = ctx.createRadialGradient(
canvas.width/2, canvas.height/2, 0,
canvas.width/2, canvas.height/2, canvas.width
);
gradient.addColorStop(0, '#000000');
gradient.addColorStop(0.7, '#1a1a2e');
gradient.addColorStop(1, '#16213e');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Add energetic gold particles
ctx.fillStyle = 'rgba(212, 175, 55, 0.3)';
for (let i = 0; i < 150; i++) {
const size = Math.random() * 5 + 2;
const x = Math.random() * canvas.width;
const y = Math.random() * canvas.height;
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fill();
}
// Add glowing title effect (adjusted font size for better fit)
ctx.shadowColor = '#f9d423';
ctx.shadowBlur = 30;
ctx.fillStyle = '#f9d423';
ctx.font = 'bold 70px "Arial", sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// Measure text width and adjust if needed
const titleText = '🔥 WORKOUT COMPLETE 🔥';
const maxTitleWidth = canvas.width - 100;
let titleFontSize = 70;
// Scale down if too wide
while (ctx.measureText(titleText).width > maxTitleWidth && titleFontSize > 40) {
titleFontSize -= 2;
ctx.font = `bold ${titleFontSize}px "Arial", sans-serif`;
}
ctx.fillText(titleText, canvas.width/2, 120);
ctx.shadowBlur = 0;
// Add animated burst effect behind title
ctx.fillStyle = 'rgba(249, 212, 35, 0.2)';
for (let i = 0; i < 8; i++) {
const angle = (i / 8) * Math.PI * 2;
const length = 150;
const x1 = canvas.width/2;
const y1 = canvas.height/2 - 180;
const x2 = x1 + Math.cos(angle) * length;
const y2 = y1 + Math.sin(angle) * length;
const gradient = ctx.createLinearGradient(x1, y1, x2, y2);
gradient.addColorStop(0, 'rgba(249, 212, 35, 0.3)');
gradient.addColorStop(1, 'rgba(249, 212, 35, 0)');
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x2 + Math.cos(angle + 0.2) * 20, y2 + Math.sin(angle + 0.2) * 20);
ctx.lineTo(x2 + Math.cos(angle - 0.2) * 20, y2 + Math.sin(angle - 0.2) * 20);
ctx.closePath();
ctx.fill();
}
// Stats container with perfectly centered content
const stats = [
{ icon: '⏱️', text: 'Duration', value: '45:22', change: '+2:18' },
{ icon: '🔥', text: 'Calories', value: '412', change: 'Record!' },
{ icon: '💪', text: 'Exercises', value: '8', change: '+1 new' },
{ icon: '📈', text: 'Progress', value: '3/5', change: '60%' }
];
const cardWidth = 220;
const cardHeight = 140;
const startX = (canvas.width - (cardWidth * 2 + 30)) / 2;
stats.forEach((stat, i) => {
const row = Math.floor(i / 2);
const col = i % 2;
const x = startX + col * (cardWidth + 40);
const y = 200 + row * (cardHeight + 20);
// Card background
ctx.fillStyle = 'rgba(40, 40, 60, 0.8)';
ctx.beginPath();
ctx.roundRect(x, y, cardWidth, cardHeight, 20);
ctx.fill();
// Gold border effect
ctx.strokeStyle = 'rgba(212, 175, 55, 0.5)';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.roundRect(x, y, cardWidth, cardHeight, 20);
ctx.stroke();
// Card content with perfect vertical centering
const contentHeight = 30 + 20 + 36 + 18 + 10; // icon + label + value + change + spacing
const startY = y + (cardHeight - contentHeight) / 2;
// Icon (centered)
ctx.fillStyle = '#f9d423';
ctx.font = 'bold 30px "Arial", sans-serif';
const iconX = x + cardWidth / 2;
ctx.textAlign = 'center';
ctx.fillText(stat.icon, iconX, startY + 30);
// Stat label (centered)
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 20px "Arial", sans-serif';
ctx.fillText(stat.text, iconX, startY + 30 + 20 + 5);
// Main value (centered)
ctx.fillStyle = '#f9d423';
ctx.font = 'bold 36px "Arial", sans-serif';
ctx.fillText(stat.value, iconX, startY + 30 + 20 + 5 + 36);
// Change indicator (centered)
ctx.fillStyle = '#4ade80';
ctx.font = 'bold 18px "Arial", sans-serif';
const changeText = stat.change.length > 10 ? stat.change.substring(0, 8) + '...' : stat.change;
ctx.fillText(changeText, iconX, startY + 30 + 20 + 5 + 36 + 18);
ctx.textAlign = 'left'; // Reset alignment
});
// Circular progress chart (moved lower)
const progressX = canvas.width/2;
const progressY = 650; // Higher position
const radius = 100;
const progress = 0.6; // 60%
// Background circle
ctx.beginPath();
ctx.arc(progressX, progressY, radius, 0, Math.PI * 2);
ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
ctx.lineWidth = 20;
ctx.stroke();
// Progress arc
ctx.beginPath();
ctx.arc(progressX, progressY, radius, -Math.PI/2, -Math.PI/2 + Math.PI * 2 * progress);
ctx.strokeStyle = '#f9d423';
ctx.lineWidth = 20;
ctx.lineCap = 'round';
ctx.stroke();
// Progress text (centered below ring)
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 36px "Arial", sans-serif';
ctx.fillText(`${Math.round(progress * 100)}%`, progressX, progressY + radius + 60);
// Motivational text (centered below progress)
ctx.shadowColor = '#f9d423';
ctx.shadowBlur = 15;
ctx.fillStyle = '#ffffff';
const motivationalText = 'CRUSHED IT!';
let motivationalSize = 36;
ctx.font = `italic ${motivationalSize}px "Arial", sans-serif`;
while (ctx.measureText(motivationalText).width > canvas.width - 120 && motivationalSize > 24) {
motivationalSize -= 2;
ctx.font = `italic ${motivationalSize}px "Arial", sans-serif`;
}
const motivationalY = progressY + radius + 120;
ctx.fillText(motivationalText, canvas.width/2, motivationalY);
ctx.shadowBlur = 0;
// Logo with gold gradient (centered at bottom)
const logoGradient = ctx.createLinearGradient(0, canvas.height - 180, 0, canvas.height - 100);
logoGradient.addColorStop(0, '#f9d423');
logoGradient.addColorStop(1, '#d4af37');
ctx.fillStyle = logoGradient;
ctx.font = 'bold 48px "Arial", sans-serif';
ctx.fillText('KINTSUGI', canvas.width/2, canvas.height - 130);
// Tagline (centered below logo)
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
ctx.font = '24px "Arial", sans-serif';
ctx.fillText('Transform through movement', canvas.width/2, canvas.height - 80);
// Add decorative gold elements
ctx.fillStyle = 'rgba(212, 175, 55, 0.1)';
ctx.beginPath();
ctx.ellipse(100, 200, 150, 60, Math.PI/4, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.ellipse(canvas.width - 100, canvas.height - 150, 120, 50, -Math.PI/4, 0, Math.PI * 2);
ctx.fill();
// Convert canvas to image and download with timestamp
const now = new Date();
const timestamp = `${now.getFullYear()}${(now.getMonth()+1).toString().padStart(2,'0')}${now.getDate().toString().padStart(2,'0')}_${now.getHours().toString().padStart(2,'0')}${now.getMinutes().toString().padStart(2,'0')}`;
canvas.toBlob((blob) => {
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.download = `kintsugi_workout_${timestamp}.png`;
link.href = url;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
// Show success notification
const notification = document.createElement('div');
notification.className = 'fixed bottom-4 left-1/2 transform -translate-x-1/2 bg-green-600 text-white px-4 py-2 rounded-lg shadow-lg flex items-center';
notification.innerHTML = `
<i class="fas fa-check-circle mr-2"></i>
<span>Saved to Photos as kintsugi_workout_${timestamp}.png</span>
`;
document.body.appendChild(notification);
// Remove notification after 3 seconds
setTimeout(() => {
notification.remove();
}, 3000);
}, 'image/png', 1);
});
// Trends modal functionality
const trendsModal = document.getElementById('trendsModal');
const closeTrendsModalBtn = document.getElementById('closeTrendsModalBtn');
document.querySelector('button:has(.fa-chart-line)').addEventListener('click', () => {
trendsModal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
});
closeTrendsModalBtn.addEventListener('click', () => {
trendsModal.classList.add('hidden');
document.body.style.overflow = 'auto';
});
// Close modal when clicking outside
trendsModal.addEventListener('click', (e) => {
if (e.target === trendsModal) {
trendsModal.classList.add('hidden');
document.body.style.overflow = 'auto';
}
});
</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=j1225d/workout-complete" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>