Running Training Schedule Creator

Running Training Schedule Creator

Race and Goal Setting

e.g., Long Run, Speed Work, Tempo, Strength Training.

Weekly Training Schedule

Review your complete training schedule and calculations.

Click "Next" or "Previous" to refresh this preview.

Calculated Weekly Mileage: ${data.totals.totalMileage.toFixed(1)} miles

Goal Status: ${data.totals.totalMileage >= data.totals.peakMileage ? 'GOAL MET' : 'BELOW TARGET'}

Key Workouts: ${escapeHTML(data.keyWorkouts).replace(/\n/g, '
')}

2. Detailed Weekly Schedule

${scheduleHTML}
Day Distance Workout Type
`; }; const downloadTxt = () => { const data = getPlanData(); const totals = data.totals; let content = `RUNNING TRAINING SCHEDULE: ${data.raceName.toUpperCase()}\n`; content += "========================================================\n\n"; content += "1. GOALS & SUMMARY\n"; content += "--------------------------------------------------------\n"; content += `Plan Duration: ${data.duration} Weeks\n`; content += `Target Peak Mileage: ${data.goalMileage} miles\n`; content += `Calculated Weekly Mileage: ${totals.totalMileage.toFixed(1)} miles\n`; content += `Status: ${totals.totalMileage >= totals.peakMileage ? 'GOAL MET' : 'BELOW TARGET'}\n`; content += `Key Workouts:\n${data.keyWorkouts}\n\n`; content += "2. WEEKLY SCHEDULE\n"; content += "--------------------------------------------------------\n"; data.schedule.forEach(item => { content += `${item.day.padEnd(12)}: ${item.distance.toFixed(1).padEnd(5)} miles | ${WORKOUT_TYPES.find(wt => wt.key === item.type)?.label || item.type}\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 = `training_plan_${data.raceName.replace(/ /g, '_')}.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') { alert('Error: jsPDF library not loaded.'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF('p', 'mm', 'a4'); const data = getPlanData(); const totals = data.totals; const margin = 15; let yPos = 20; const pageWidth = doc.internal.pageSize.getWidth(); const addTitle = (text, size, color, style = 'bold') => { if (yPos > 280) { doc.addPage(); yPos = 20; } doc.setFontSize(size); doc.setFont(undefined, style); doc.setTextColor(color[0], color[1], color[2]); doc.text(text, margin, yPos); yPos += size / 2 + 3; }; const addText = (text, size = 10, style = 'normal', indent = 0) => { doc.setFontSize(size); doc.setFont(undefined, style); doc.setTextColor(52, 73, 94); const lines = doc.splitTextToSize(text, pageWidth - margin * 2 - indent); if (lines.length * 5 + yPos > 280) { doc.addPage(); yPos = 20; } doc.text(lines, margin + indent, yPos); yPos += (lines.length * 5) + 3; }; // 1. Header doc.setFontSize(20); doc.setFont(undefined, 'bold'); doc.setTextColor(44, 62, 80); doc.text(`Running Training Schedule`, pageWidth / 2, yPos, { align: 'center' }); yPos += 8; doc.setFontSize(14); doc.setTextColor(22, 160, 133); doc.text(data.raceName, pageWidth / 2, yPos, { align: 'center' }); yPos += 10; // 2. Goals Summary addTitle("1. Key Goals & Summary", 12, [22, 160, 133]); const totalStats = [ ['Plan Duration:', `${data.duration} Weeks`], ['Target Peak Mileage:', `${data.goalMileage} miles`], ['Calculated Weekly Mileage:', `${totals.totalMileage.toFixed(1)} miles`], ['Status:', totals.totalMileage >= totals.peakMileage ? 'GOAL MET' : 'BELOW TARGET'] ]; doc.autoTable({ startY: yPos, body: totalStats, theme: 'plain', styles: { fontSize: 10, cellPadding: 2, textColor: [52, 73, 94] }, columnStyles: { 0: { fontStyle: 'bold', cellWidth: 50 }, 1: { fontStyle: 'bold', textColor: [22, 160, 133] } }, margin: { left: margin, right: pageWidth - 100 } }); yPos = doc.autoTable.previous.finalY + 5; addText("Key Workouts:", 10, 'bold'); addText(data.keyWorkouts, 10, 'normal', 5); yPos += 3; // 3. Detailed Weekly Schedule addTitle("2. Detailed Weekly Schedule", 12, [22, 160, 133]); const scheduleHead = [['Day', 'Distance (Miles)', 'Workout Type']]; const scheduleBody = data.schedule.map(item => [ item.day, item.distance.toFixed(1), WORKOUT_TYPES.find(wt => wt.key === item.type)?.label || item.type ]); doc.autoTable({ startY: yPos, head: scheduleHead, body: scheduleBody, theme: 'grid', styles: { fontSize: 10, cellPadding: 3, textColor: [52, 73, 94] }, headStyles: { fillColor: [22, 160, 133], textColor: [255, 255, 255] }, columnStyles: { 0: { fontStyle: 'bold', cellWidth: 40 } }, margin: { left: margin, right: margin } }); yPos = doc.autoTable.previous.finalY + 5; doc.save(`training_plan_${data.raceName.replace(/ /g, '_')}.pdf`); }; // --- Calculation Helper --- const calculateTotals = () => { const totalMileage = weeklySchedule.reduce((sum, item) => sum + item.distance, 0); const peakMileage = parseFloat(goalInputs.goalMileage.value) || 0; return { totalMileage, peakMileage }; }; // --- Event Listeners --- // Tab Buttons tabButtons.forEach((btn, index) => { btn.addEventListener('click', () => showTab(index + 1)); }); // Next/Prev Navigation nextBtn.addEventListener('click', () => showTab(currentTab + 1)); prevBtn.addEventListener('click', () => showTab(currentTab - 1)); // Tab 2: Live interaction for schedule changes scheduleGrid.addEventListener('change', (e) => { if (e.target.dataset.day) { updateSchedule(e.target.dataset.day, e.target.dataset.field, e.target.value); } }); scheduleGrid.addEventListener('keyup', (e) => { if (e.target.dataset.day && e.target.type === 'number') { updateSchedule(e.target.dataset.day, e.target.dataset.field, e.target.value); } }); // Tab 3 Actions downloadPdfBtn.addEventListener('click', downloadPDF); downloadTxtBtn.addEventListener('click', downloadTxt); // --- Initialization --- showTab(1); });
Scroll to Top