Fitness Workout Plan Generator
Plan Fundamentals
Define Exercises and Default Sets/Reps
Current Exercise Library (Editable)
| Exercise Name | Sets | Reps | Default Weight (lb) | Actions |
|---|
Assign Exercises to Days
Select an exercise and a day to build your workout split.
Weekly Split Preview
Review the final structured workout plan.
Generate the plan to see the export preview.
Primary Goal: ${escapeHTML(data.setup.goal)} | Duration: ${escapeHTML(data.setup.duration)}
Plan Notes / Philosophy
${escapeHTML(data.setup.notes)}
Weekly Workout Split
${scheduleHTML}
`;
};
const downloadTxt = () => {
const data = getPlanData();
let content = `WORKOUT PLAN: ${data.setup.name.toUpperCase()}\n`;
content += "========================================\n";
content += `Goal: ${data.setup.goal}\nDuration: ${data.setup.duration}\n\n`;
content += `NOTES: ${data.setup.notes}\n`;
content += "========================================\n\n";
dayNames.forEach(day => {
content += `--- ${day.toUpperCase()} ---\n`;
const dayContent = data.schedule[day];
if (dayContent && dayContent.length > 0) {
dayContent.forEach(exId => {
const ex = getExerciseById(exId);
if (ex) {
content += `${ex.name} - ${ex.sets} sets x ${ex.reps} reps @ ${ex.weight} lb\n`;
}
});
} else {
content += `[Rest Day or Empty]\n`;
}
content += "\n";
});
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `${data.setup.name.replace(/ /g, '_')}_plan.txt`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(a.href);
};
const downloadPDF = () => {
if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined' || typeof window.jspdf.autoTable === 'undefined') {
alert('Error: jsPDF libraries not loaded.');
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF('p', 'mm', 'a4');
const data = getPlanData();
const margin = 15;
const pageWidth = doc.internal.pageSize.getWidth();
const usableWidth = pageWidth - margin * 2;
let yPos = 15;
// --- PDF Helper ---
const addText = (text, size, style, color = [52, 73, 94], align = 'left', indent = 0) => {
doc.setFontSize(size);
doc.setFont(undefined, style);
doc.setTextColor(color[0], color[1], color[2]);
const splitText = doc.splitTextToSize(text, usableWidth - indent);
if (yPos + (splitText.length * 5) > 280) { doc.addPage(); yPos = 20; }
doc.text(splitText, margin + indent, yPos);
yPos += (splitText.length * 5) + (size > 12 ? 3 : 1);
};
const addSectionHeader = (title) => {
yPos += 5;
doc.setFontSize(14);
doc.setFont(undefined, 'bold');
doc.setTextColor(231, 76, 60); // Red
doc.text(title, margin, yPos);
doc.setDrawColor(224, 224, 224);
doc.line(margin, yPos + 1, pageWidth - margin, yPos + 1);
yPos += 8;
};
// 1. Title Block
addText(data.setup.name.toUpperCase(), 18, 'bold', [44, 62, 80], 'center');
addText(`Goal: ${data.setup.goal} | Duration: ${data.setup.duration}`, 10, 'normal', [108, 117, 125], 'center');
yPos += 5;
// 2. Notes
addSectionHeader("Plan Notes / Philosophy");
addText(data.setup.notes, 10, 'normal', [52, 73, 94], 'left', 5);
yPos += 5;
// 3. Weekly Schedule
addSectionHeader("Weekly Workout Split");
dayNames.forEach(day => {
const dayContent = data.schedule[day];
doc.setFontSize(12);
doc.setFont(undefined, 'bold');
doc.setTextColor(44, 62, 80);
doc.text(day, margin, yPos);
yPos += 6;
if (dayContent && dayContent.length > 0) {
dayContent.forEach(exId => {
const ex = getExerciseById(exId);
if (ex) {
const line = `${ex.name}: ${ex.sets} sets x ${ex.reps} reps @ ${ex.weight} lb`;
addText(line, 10, 'normal', [52, 73, 94], 'left', 10);
}
});
} else {
addText('[REST DAY / ACTIVE RECOVERY]', 10, 'italic', [108, 117, 125], 'left', 10);
}
yPos += 5;
});
doc.save('workout_plan.pdf');
};
// --- Event Listeners ---
// Global Navigation
tabButtons.forEach((btn, index) => {
btn.addEventListener('click', () => showTab(index + 1));
});
nextBtn.addEventListener('click', () => showTab(currentTab + 1));
prevBtn.addEventListener('click', () => showTab(currentTab - 1));
// Tab 2 Actions (Add, Remove, Edit)
exInputs.addBtn.addEventListener('click', addExercise);
exInputs.tbody.addEventListener('click', (e) => {
if (e.target.dataset.removeId) {
removeExercise(parseInt(e.target.dataset.removeId));
}
});
exInputs.tbody.addEventListener('blur', (e) => {
if (e.target.tagName === 'TD' && e.target.isContentEditable) {
updateExercise(parseInt(e.target.dataset.id), e.target.dataset.field, e.target.textContent);
}
}, true);
// Tab 3 Actions
scheduleInputs.assignBtn.addEventListener('click', assignExerciseToDay);
// Tab 4 Actions
refreshBtn.addEventListener('click', generatePreview);
downloadPdfBtn.addEventListener('click', downloadPDF);
downloadTxtBtn.addEventListener('click', downloadTxt);
// --- Initialization ---
showTab(1); // Set initial state
});
