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();
});