Theme: ${data.theme}
Instructor: ${data.instructor} |
Level: ${data.level} |
Target Duration: ${data.duration} Minutes
`;
// 2. Sequence Table Grouped by Category
const groupedSequence = data.sequence.reduce((acc, pose) => {
acc[pose.category] = acc[pose.category] || [];
acc[pose.category].push(pose);
return acc;
}, {});
let poseCount = 1;
for (const category in groupedSequence) {
html += `
${category}
`;
let tableHtml = `
`;
groupedSequence[category].forEach(pose => {
const rowColor = poseCount % 2 === 0 ? '#f8f8f8' : '#fff';
tableHtml += `
| ${poseCount++}. |
${pose.pose} |
${pose.time} |
(Modifications/Cues) |
`;
});
tableHtml += `
`;
html += tableHtml;
}
// 3. Notes
html += `
Notes & Music Cues
`;
container.innerHTML = html;
}
function ysspSwitchTab(tabId) {
document.querySelectorAll('.yssp-tab-btn').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.yssp-content').forEach(c => c.classList.remove('active'));
const idx = tabId === 'builder' ? 0 : 1;
document.querySelectorAll('.yssp-tab-btn')[idx].classList.add('active');
document.getElementById('yssp-' + tabId).classList.add('active');
if (tabId === 'preview') {
ysspRenderSheet();
}
}
/* --- PDF Generation --- */
async function ysspGeneratePDF() {
ysspRenderSheet(); // Final render check
const data = {
theme: document.getElementById('inp-theme').value || "Class Theme",
instructor: document.getElementById('inp-instructor').value || "N/A",
duration: document.getElementById('inp-duration').value || "60",
level: document.getElementById('inp-level').value,
sequence: ysspGetSequenceData()
};
if (data.sequence.length === 0) {
alert("Please add poses to the sequence before generating the PDF.");
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF('p', 'mm', 'a4');
const brown = [93, 64, 55];
let y = 20;
// Header
doc.setFillColor(...brown);
doc.rect(0, 0, 210, 20, 'F');
doc.setTextColor(255, 255, 255);
doc.setFontSize(16);
doc.text(`Yoga Class Sequence Plan`, 14, 13);
// Metadata
doc.setTextColor(0, 0, 0);
doc.setFontSize(10);
doc.text(`Theme: ${data.theme}`, 14, y + 15);
doc.text(`Instructor: ${data.instructor}`, 100, y + 15);
doc.text(`Level: ${data.level} | Duration: ${data.duration} min`, 14, y + 20);
y += 35;
// Sequence Table
doc.setFontSize(14);
doc.setFont("helvetica", "bold");
doc.setTextColor(...brown);
doc.text("Asana Sequence & Timing", 14, y);
y += 5;
// Group data for hierarchical printing
const groupedData = data.sequence.reduce((acc, item) => {
let category = acc.find(c => c.category === item.category);
if (!category) {
category = { category: item.category, poses: [] };
acc.push(category);
}
category.poses.push([
item.step,
item.pose,
item.time,
"" // Placeholder for Cues/Modifications
]);
return acc;
}, []);
let tableY = y;
let runningStep = 0;
groupedData.forEach(group => {
if (tableY > 260) { doc.addPage(); tableY = 20; }
// Add Category Header Row
doc.autoTable({
startY: tableY,
head: [[{ content: group.category.toUpperCase(), colSpan: 4 }]],
theme: 'grid',
headStyles: { fillColor: [240, 240, 240], textColor: [0, 0, 0], fontSize: 10 },
columnStyles: { 0: { cellWidth: 182 } },
margin: { left: 14, right: 14 }
});
tableY = doc.lastAutoTable.finalY;
// Add Poses
doc.autoTable({
startY: tableY,
head: [['#', 'Pose Name', 'Time', 'Notes/Modifications']],
body: group.poses,
theme: 'grid',
styles: { fontSize: 9 },
headStyles: { fillColor: [200, 200, 200], textColor: [0, 0, 0], fontSize: 8 },
columnStyles: {
0: { cellWidth: 8, halign: 'center', fontStyle: 'bold' },
2: { cellWidth: 20, fontStyle: 'bold' },
3: { cellWidth: 'auto', overflow: 'linebreak' }
},
margin: { left: 14, right: 14 }
});
tableY = doc.lastAutoTable.finalY;
runningStep += group.poses.length;
});
// Final Signature Block
let finalY = tableY + 20;
if (finalY > 270) { doc.addPage(); finalY = 30; }
doc.setFontSize(10);
doc.text("Instructor Signature: ___________________________", 14, finalY);
doc.save(`YogaSequence_${data.theme.replace(/\s/g, '_')}.pdf`);
}