`;
// 2. Attendance Table
if (data.roster.length === 0 || numDates === 0) {
html += '
`;
}
// 3. Key
html += `
Add names and dates in the builder to generate the table.
'; } else { html += `| # | Participant Name / ID | ${dateHeaders}TOTAL | ').join(''); html += ` |
|---|---|---|
| ${index + 1} | ${name} | ${markColumns}
Key: P=Present, A=Absent, L=Late.
`;
container.innerHTML = html;
}
function asgSwitchTab(tabId) {
document.querySelectorAll('.asg-tab-btn').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.asg-content').forEach(c => c.classList.remove('active'));
const idx = tabId === 'builder' ? 0 : 1;
document.querySelectorAll('.asg-tab-btn')[idx].classList.add('active');
document.getElementById('asg-' + tabId).classList.add('active');
if (tabId === 'preview') {
asgRenderSheet();
}
}
function asgLoadExample() {
if(!confirm("Overwrite current data with example workshop data?")) return;
document.getElementById('inp-course').value = "Fall UX Design Workshop";
document.getElementById('inp-instructor').value = "M. Chen / Design Lead";
// Clear and fill dates/roster
document.getElementById('asg-date-rows-container').innerHTML = '';
document.getElementById('asg-roster-rows-container').innerHTML = '';
asgAddDate('Day 1 - Intro');
asgAddDate('Day 2 - Prototyping');
asgAddDate('2025-11-20');
asgAddDate('2025-11-27');
asgAddDate('Final Review');
asgAddRosterName('Johnson, Liam');
asgAddRosterName('Patel, Ananya');
asgAddRosterName('Vance, Robert');
asgRenderSheet();
asgSwitchTab('preview');
}
/* --- PDF Generation --- */
async function asgGeneratePDF() {
asgRenderSheet(); // Final render check
const data = asgGetSheetData();
if (data.roster.length === 0 || data.dates.length === 0) {
alert("Please add participants and session dates before generating the PDF.");
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF('l', 'mm', 'a4'); // Landscape for more columns
const navy = [44, 62, 80];
let y = 20;
// 1. Header
doc.setFillColor(...navy);
doc.rect(0, 0, 297, 20, 'F');
doc.setTextColor(255, 255, 255);
doc.setFontSize(16);
doc.text(`Attendance Roster: ${data.course}`, 14, 13);
// 2. Metadata
doc.setTextColor(0, 0, 0);
doc.setFontSize(10);
doc.text(`Instructor: ${data.instructor}`, 14, y + 10);
doc.text(`Total Sessions: ${data.dates.length}`, 100, y + 10);
y += 20;
// 3. Table Headers
const columnHeaders = ['#', 'Participant Name'].concat(data.dates).concat(['TOTAL']);
// Create empty body rows
const tableBody = data.roster.map((name, index) => {
return [index + 1, name].concat(Array(data.dates.length).fill('')).concat(['']);
});
doc.autoTable({
startY: y,
head: [columnHeaders],
body: tableBody,
theme: 'grid',
headStyles: { fillColor: [230, 236, 240], textColor: navy, fontSize: 8.5 },
styles: { fontSize: 8, cellPadding: 2.5 },
columnStyles: {
0: { cellWidth: 8, fontStyle: 'bold', halign: 'center' },
1: { cellWidth: 50, fontStyle: 'bold' },
// Set fixed width for attendance columns
...data.dates.reduce((acc, _, index) => {
acc[index + 2] = { cellWidth: 15, halign: 'center' };
return acc;
}, {})
}
});
// 4. Key
let finalY = doc.internal.pageSize.height - 10;
doc.setFontSize(8);
doc.text("Key: P = Present, A = Absent, L = Late. (Please indicate total sessions missed in the final column)", 14, finalY);
doc.save(`Attendance_Roster_${data.course.replace(/\s/g, '_')}.pdf`);
}
