No terms added yet.
";
return;
}
vocabulary.forEach(t => {
const itemEl = document.createElement("div");
itemEl.className = "svwg-config-list-item";
itemEl.dataset.id = t.id;
itemEl.innerHTML = `
${escapeHTML(t.topic)}: ${escapeHTML(t.term)} - ${escapeHTML(t.def.substring(0, 30))}...
`;
configTermList.appendChild(itemEl);
});
}
// --- Dashboard Management ---
function renderDashboard(isInitial = true) {
// Get config headers
dashTitle.value = configTitle.value;
dashGrade.value = configGrade.value;
// --- Build Dashboard Table HTML ---
if (vocabulary.length === 0) {
dashboardOutput.innerHTML = `
Add terms and definitions using the "Term Configuration" tab and then generate the worksheet here.
`;
return;
}
dashboardOutput.innerHTML = `
| # |
Vocabulary Term |
Definition |
Topic |
Action |
`;
// Populate Table Body
const tbody = dashboardOutput.querySelector('#svwg-dash-table tbody');
vocabulary.forEach((t, index) => {
const tr = document.createElement('tr');
tr.dataset.id = t.id;
tr.innerHTML = `
${index + 1} |
|
|
|
|
`;
tbody.appendChild(tr);
});
setupDashboardListeners();
if (!isInitial) showTab('svwg-tab-dashboard');
}
/**
* Attaches listeners to the dashboard elements
*/
function setupDashboardListeners() {
const table = dashboardOutput.querySelector('#svwg-dash-table');
if (!table) return;
// Event delegation for removal
table.addEventListener('click', (e) => {
if (e.target.dataset.action === 'remove-term') {
const tr = e.target.closest('tr');
vocabulary.splice(vocabulary.findIndex(t => t.id === tr.dataset.id), 1);
// Re-render entirely to renumber the questions
renderDashboard(false);
updateConfigTermDisplay();
}
});
// Event delegation for input/change updates
table.addEventListener('input', handleDashboardUpdate);
table.addEventListener('change', handleDashboardUpdate);
}
/**
* Handles updates made directly to the dashboard table cells
*/
function handleDashboardUpdate(e) {
const target = e.target;
const tr = target.closest('tr');
if (!tr) return;
const tId = tr.dataset.id;
const term = vocabulary.find(t => t.id === tId);
if (!term) return;
const field = target.dataset.field;
if (!field) return;
// Update the master state array
term[field] = target.value;
// Optional: Update config list display immediately
updateConfigTermDisplay();
}
/**
* Generates a PDF report from the dashboard data
*/
function downloadPDF() {
if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF.autoTable === 'undefined') {
alert("Error: PDF libraries could not be loaded. Please try again.");
return;
}
const table = dashboardOutput.querySelector('#svwg-dash-table');
if (!table) {
alert("Please generate the worksheet before downloading.");
return;
}
try {
const { jsPDF } = window.jspdf;
const worksheetDoc = new jsPDF("p", "pt", "a4");
const answerKeyDoc = new jsPDF("p", "pt", "a4");
const margin = 40;
const title = dashTitle.value || "Science Vocabulary Worksheet";
const grade = dashGrade.value || "Level/Subject";
const tableHead = [['#', 'Term', 'Definition/Description', 'Topic']];
const tableBody = [];
const tableBodyKey = [];
// 1. Extract data from the editable dashboard table
Array.from(table.querySelectorAll('tbody tr')).forEach((tr, index) => {
const term = tr.querySelector('[data-field="term"]').value;
const def = tr.querySelector('[data-field="def"]').value;
const topic = tr.querySelector('[data-field="topic"]').value;
tableBody.push([
index + 1,
term,
'________________________________________________________', // Blank space for definition
topic
]);
tableBodyKey.push([
index + 1,
term,
def,
topic
]);
});
// --- Worksheet PDF (Term/Blank Definition) ---
worksheetDoc.setFontSize(18);
worksheetDoc.setFont(undefined, 'bold');
worksheetDoc.text(title, worksheetDoc.internal.pageSize.getWidth() / 2, margin, { align: 'center' });
worksheetDoc.setFontSize(12);
worksheetDoc.setFont(undefined, 'normal');
worksheetDoc.text(`Subject/Level: ${grade}`, margin, margin + 30);
worksheetDoc.text('Name: ______________________', worksheetDoc.internal.pageSize.getWidth() - margin, margin + 30, { align: 'right' });
worksheetDoc.autoTable({
startY: margin + 60,
head: tableHead,
body: tableBody.map(row => [row[0], row[1], row[2], row[3]]), // Use term and blank space
theme: 'grid',
headStyles: { fillColor: [0, 115, 230], textColor: [255, 255, 255], fontSize: 10 },
styles: { fontSize: 9, cellPadding: 8, overflow: 'linebreak' },
columnStyles: { 0: { cellWidth: 30, halign: 'center' }, 1: { cellWidth: 100 }, 2: { cellWidth: 'auto' }, 3: { cellWidth: 70 } },
margin: { left: margin, right: margin }
});
worksheetDoc.save(`${title.replace(/ /g,"_")}_Worksheet.pdf`);
// --- Answer Key PDF (Term/Definition) ---
answerKeyDoc.setFontSize(18);
answerKeyDoc.setFont(undefined, 'bold');
answerKeyDoc.text(`${title} - ANSWER KEY`, answerKeyDoc.internal.pageSize.getWidth() / 2, margin, { align: 'center' });
answerKeyDoc.setFontSize(12);
answerKeyDoc.setFont(undefined, 'normal');
answerKeyDoc.text(`Subject/Level: ${grade}`, margin, margin + 30);
answerKeyDoc.autoTable({
startY: margin + 50,
head: tableHead,
body: tableBodyKey, // Use Term and Definition
theme: 'grid',
headStyles: { fillColor: [40, 167, 69], textColor: [255, 255, 255], fontSize: 10 },
styles: { fontSize: 9, cellPadding: 8, overflow: 'linebreak' },
columnStyles: { 0: { cellWidth: 30, halign: 'center' }, 1: { cellWidth: 100 }, 2: { cellWidth: 'auto' }, 3: { cellWidth: 70 } },
margin: { left: margin, right: margin }
});
answerKeyDoc.save(`${title.replace(/ /g,"_")}_AnswerKey.pdf`);
} catch (error) {
console.error("PDF Generation Error:", error);
alert("An error occurred during PDF creation. Please ensure all data fields are filled correctly.");
}
}
/**
* Helper to escape HTML
*/
function escapeHTML(str) {
if (!str) return "";
return str
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// --- 4. INITIALIZATION & EVENT LISTENERS ---
// Tab Listeners
tabButtons.forEach((btn) => {
btn.addEventListener("click", () => showTab(btn.dataset.target));
});
navButtons.forEach((btn) => {
btn.addEventListener("click", () => showTab(btn.dataset.target));
});
// Config Tab Listeners
if (addTermForm) {
addTermForm.addEventListener("submit", handleAddTerm);
}
if (configTermList) {
configTermList.addEventListener("click", handleRemoveTerm);
}
if (generateBtn) {
generateBtn.addEventListener("click", () => renderDashboard(false));
}
// PDF Button
if (pdfBtn) {
pdfBtn.addEventListener("click", downloadPDF);
}
// Initial config list display
updateConfigTermDisplay();
// Initial State: Generate dashboard with samples
renderDashboard();
showTab("svwg-tab-dashboard");
});