Fitness Progress Tracker Generator

Fitness Progress Tracker Generator

Fitness Progress Tracker Generator

1. Personal Profile & Goals

2. Exercise Library

Available Exercises

    No exercises added yet.

3. Weekly Training Schedule

Paste the Name/Sets/Reps of exercises into the boxes below (one per line, e.g., Squats 3x10). Use 'REST' for off days.

Mon
Tue
Wed
Thu
Fri
Sat
Sun

4. Progress Tracking Log

Date Weight (lbs) Key Metric Notes / Context Action
Tracked data will appear here.

No exercises added yet.

'; return; } exercises.forEach((ex, index) => { const li = document.createElement('li'); li.className = 'fpg-list-item'; li.innerHTML = ` ${ex.name} (${ex.sets}) `; li.querySelector('button').addEventListener('click', () => removeExercise(index)); exerciseList.appendChild(li); }); } function addExercise() { const name = exerciseNameInput.value.trim(); const sets = defaultSetsInput.value.trim(); if (!name || !sets) { alert("Please enter both an Exercise Name and Default Sets/Reps."); return; } if (exercises.some(ex => ex.name.toLowerCase() === name.toLowerCase())) { alert("This exercise is already in your library."); return; } exercises.push({ name, sets }); renderExercises(); // Clear form exerciseNameInput.value = ''; defaultSetsInput.value = '3x10'; } function removeExercise(index) { exercises.splice(index, 1); renderExercises(); } // --- Progress Log Management (NEW LOGIC) --- function renderProgressLog() { logTableBody.innerHTML = ''; if (progressLog.length === 0) { logTableBody.innerHTML = 'Tracked data will appear here.'; return; } // Sort by date descending progressLog.sort((a, b) => new Date(b.date) - new Date(a.date)); progressLog.forEach((log) => { const row = document.createElement('tr'); row.innerHTML = ` ${log.date} ${log.weight} lbs ${log.metric} ${log.notes} `; logTableBody.appendChild(row); }); } function addLogEntry() { const date = logDateInput.value.trim(); const weight = parseFloat(logWeightInput.value); const metric = logMetricInput.value.trim(); const notes = logNotesInput.value.trim(); if (!date || isNaN(weight) || !metric) { alert("Please enter a Date, Weight, and Key Metric/Achievement."); return; } progressLog.push({ id: generateId(), date, weight, metric, notes: notes || 'N/A' }); renderProgressLog(); // Clear inputs logWeightInput.value = ''; logMetricInput.value = ''; logNotesInput.value = ''; } function removeLogEntry(id) { progressLog = progressLog.filter(log => log.id !== id); renderProgressLog(); } // --- PDF Generation (Updated to include Progress Log) --- function downloadPDF() { if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { alert('Error: jsPDF library loaded.'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF('landscape', 'pt', 'a4'); const margin = 40; const pageWidth = doc.internal.pageSize.getWidth(); let currentY = margin; // --- Helper for Page Break Check --- function checkPageBreak(heightNeeded) { const pageHeight = doc.internal.pageSize.getHeight(); if (currentY + heightNeeded > pageHeight - margin) { doc.addPage('landscape'); currentY = margin; return true; } return false; } // --- Gather Data --- const planName = planNameInput.value || 'Untitled Fitness Plan'; const focus = focusSelect.value; const duration = durationInput.value || 'N/A'; const goalStatement = goalStatementTextarea.value || 'N/A'; // --- Helper function to add structured text sections --- function addTextSection(title, content, startY) { checkPageBreak(30); // Check before title doc.setFontSize(12); doc.setFont('helvetica', 'bold'); doc.setTextColor(13, 148, 136); // teal-600 doc.text(title, margin, startY); doc.setFontSize(10); doc.setFont('helvetica', 'normal'); doc.setTextColor(51, 65, 85); const lines = doc.splitTextToSize(content || 'N/A', pageWidth - margin * 2); doc.text(lines, margin, startY + 15); return startY + 15 + lines.length * 12 + 10; } // --- Header --- doc.setFontSize(22); doc.setFont('helvetica', 'bold'); doc.setTextColor(13, 148, 136); doc.text(planName, pageWidth / 2, currentY, { align: 'center' }); currentY += 15; doc.setFontSize(14); doc.text(`Focus: ${focus} | Duration: ${duration} Weeks`, pageWidth / 2, currentY, { align: 'center' }); currentY += 30; // --- Section 1: Goal --- currentY = addTextSection("1. Specific Goal Statement", goalStatement, currentY); // --- Section 2: Weekly Schedule --- checkPageBreak(150); doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.text("2. Weekly Training Schedule", margin, currentY); currentY += 15; const days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; const scheduleData = [days]; const contentRow = Array.from(scheduleSlots).map(slot => slot.value.trim()); scheduleData.push(contentRow); doc.autoTable({ startY: currentY, head: [scheduleData[0]], body: [scheduleData[1]], theme: 'grid', headStyles: { fillColor: [13, 148, 136], textColor: [255, 255, 255] }, styles: { fontSize: 8, minCellHeight: 60, overflow: 'linebreak' }, columnStyles: { 0: { cellWidth: 'auto' } }, margin: { left: margin, right: margin }, didDrawCell: (data) => { const content = data.cell.text.join(' ').trim(); if (content.toUpperCase() === 'REST') { doc.setFillColor(241, 245, 249); doc.rect(data.cell.x, data.cell.y, data.cell.width, data.cell.height, 'F'); } } }); currentY = doc.autoTable.previous.finalY + 20; // --- Section 3: Progress Log (NEW) --- checkPageBreak(150); doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.text("3. Progress Tracking Log", margin, currentY); currentY += 10; if (progressLog.length > 0) { const logData = progressLog.map(log => [ log.date, `${log.weight} lbs`, log.metric, log.notes ]); doc.autoTable({ startY: currentY, head: [['Date', 'Weight', 'Key Metric/Achievement', 'Notes/Context']], body: logData, theme: 'grid', headStyles: { fillColor: [13, 148, 136], textColor: [255, 255, 255] }, styles: { fontSize: 9, minCellHeight: 12, overflow: 'linebreak' }, columnStyles: { 1: { fontStyle: 'bold', cellWidth: 50 }, 2: { cellWidth: 100 }, 3: { cellWidth: 200 } }, margin: { left: margin, right: margin } }); currentY = doc.autoTable.previous.finalY + 15; } else { currentY = addTextSection("Progress Log:", "No log entries recorded yet.", currentY); } // --- Section 4: Exercise Library --- currentY = addTextSection("4. Exercise Library", "Library of defined exercises and default parameters.", currentY); if (exercises.length > 0) { const libraryData = exercises.map(ex => [ex.name, ex.sets]); doc.autoTable({ startY: currentY, head: [['Exercise', 'Default Sets x Reps']], body: libraryData, theme: 'striped', headStyles: { fillColor: [13, 148, 136] }, styles: { fontSize: 9, minCellHeight: 12 }, columnStyles: { 0: { fontStyle: 'bold' } }, margin: { left: margin, right: pageWidth / 2 + 10 } }); } doc.save('fitness-progress-plan.pdf'); } // --- Event Listeners and Initial Load --- addExerciseBtn.addEventListener('click', addExercise); logDateInput.value = new Date().toISOString().split('T')[0]; addLogBtn.addEventListener('click', addLogEntry); logTableBody.addEventListener('click', (e) => { if (e.target.classList.contains('fpg-btn-red') && e.target.dataset.id) { removeLogEntry(e.target.dataset.id); } }); exerciseList.addEventListener('click', (e) => { if (e.target.classList.contains('fpg-btn-red')) { const index = parseInt(e.target.dataset.index); removeExercise(index); } }); pdfBtn.addEventListener('click', downloadPDF); // Initial load renderExercises(); renderProgressLog(); });
Scroll to Top