${policies.replace(/\n/g, '
')}
`;
}
// --- Event Handlers ---
tabContainer.addEventListener('click', (e) => {
if (e.target.matches('.tab-button')) {
currentTabIndex = tabs.indexOf(e.target.dataset.tab);
switchTab();
}
});
nextBtn.addEventListener('click', () => {
if (currentTabIndex < tabs.length - 1) {
currentTabIndex++;
switchTab();
}
});
prevBtn.addEventListener('click', () => {
if (currentTabIndex > 0) {
currentTabIndex--;
switchTab();
}
});
addWeekBtn.addEventListener('click', () => {
syllabusData.schedule.push({ topic: '', assignments: '' });
renderSchedule();
});
addGradeBtn.addEventListener('click', () => {
syllabusData.grading.push({ grade: '', range: '' });
renderGradingScale();
});
aiButtons.forEach(btn => btn.addEventListener('click', handleAIGeneration));
// --- Navigation ---
function switchTab() {
document.querySelectorAll('.tab-button').forEach(btn => btn.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
const newTabName = tabs[currentTabIndex];
document.querySelector(`.tab-button[data-tab="${newTabName}"]`).classList.add('active');
document.getElementById(`${newTabName}Content`).classList.add('active');
updateNavButtons();
}
function updateNavButtons() {
prevBtn.style.visibility = currentTabIndex === 0 ? 'hidden' : 'visible';
nextBtn.style.visibility = currentTabIndex === tabs.length - 1 ? 'hidden' : 'visible';
}
// --- AI Generation ---
async function handleAIGeneration(e) {
const btn = e.currentTarget;
const originalContent = btn.innerHTML;
btn.innerHTML = '
';
btn.disabled = true;
const targetId = btn.dataset.target;
const promptSourceId = btn.dataset.promptSource;
const targetIndex = btn.dataset.targetIndex;
const targetField = btn.dataset.targetField;
let prompt = '';
if (targetId === 'courseDescription') {
const title = document.getElementById(promptSourceId).value;
prompt = `Generate a concise, one-paragraph course description for a university course titled "${title}".`;
} else if (targetId === 'coursePolicies') {
prompt = 'Generate a standard, generic course policy section for a university syllabus, including points on academic integrity, attendance, and late submissions. Format it with newlines.';
} else if (targetIndex !== undefined && targetField === 'topic') {
const courseTitle = syllabusData.info.title;
prompt = `For a university course titled "${courseTitle}", suggest a single, concise topic for Week ${parseInt(targetIndex)+1}.`;
}
try {
const response = await callGemini(prompt);
if (targetId) {
document.getElementById(targetId).value = response;
// Manually trigger input event to update state
document.getElementById(targetId).dispatchEvent(new Event('input'));
} else {
// For dynamic weekly topics
syllabusData.schedule[targetIndex][targetField] = response;
renderSchedule();
updateSyllabusView();
}
} catch (error) {
console.error("AI Generation Error:", error);
alert("Failed to generate content. Please try again.");
} finally {
btn.innerHTML = originalContent;
btn.disabled = false;
}
}
async function callGemini(prompt) {
const apiKey = ""; // Handled by environment
const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:generateContent?key=${apiKey}`;
const payload = { contents: [{ role: "user", parts: [{ text: prompt }] }] };
const response = await fetch(apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) throw new Error(`API Error: ${response.status}`);
const data = await response.json();
return data.candidates[0].content.parts[0].text;
}
// --- PDF Generation ---
function downloadPDF() {
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
const { info, schedule, policies, grading } = syllabusData;
const margin = 15;
const pageWidth = doc.internal.pageSize.getWidth();
let y = 0;
// Header
doc.setFont('helvetica', 'bold');
doc.setFontSize(20);
doc.text(info.title, pageWidth / 2, 20, { align: 'center' });
doc.setFontSize(14);
doc.setFont('helvetica', 'normal');
doc.text(info.code, pageWidth / 2, 28, { align: 'center' });
y = 40;
// Info
doc.setFontSize(11);
doc.text(`Instructor: ${info.instructor}`, margin, y);
doc.text(`Email: ${info.email}`, margin, y + 6);
y += 18;
// Description
doc.setFont('helvetica', 'bold');
doc.text('Course Description', margin, y);
y += 6;
doc.setFont('helvetica', 'normal');
const descLines = doc.splitTextToSize(info.description, pageWidth - margin * 2);
doc.text(descLines, margin, y);
y += descLines.length * 5 + 5;
// Schedule
doc.autoTable({
startY: y,
head: [['Week', 'Topic', 'Assignments & Readings']],
body: schedule.map((w, i) => [`Week ${i + 1}`, w.topic, w.assignments]),
headStyles: { fillColor: [37, 99, 235] }
});
y = doc.lastAutoTable.finalY + 10;
// Grading and Policies
const gradingBody = grading.map(g => [g.grade, g.range]);
doc.autoTable({
startY: y,
head: [['Grade', 'Range']],
body: gradingBody,
headStyles: { fillColor: [37, 99, 235] },
tableWidth: 80,
margin: { left: margin }
});
const policiesX = margin + 80 + 10;
doc.setFont('helvetica', 'bold');
doc.text('Course Policies', policiesX, y);
doc.setFont('helvetica', 'normal');
const policyLines = doc.splitTextToSize(policies, pageWidth - policiesX - margin);
doc.text(policyLines, policiesX, y + 8);
doc.save(`${info.code}_Syllabus.pdf`);
}
// --- Run on Load ---
initialize();
});