Waiting for the presenter to start a poll...
`;
return;
}
const hasVoted = localStorage.getItem(`voted_${poll.id}_${currentUserId}`);
const optionsHTML = poll.options.map(option => `
`).join('');
activePollContainer.innerHTML = `
${poll.question}
${optionsHTML}
${hasVoted ? '
Thanks for voting!
' : ''}
`;
}
function renderPollResults(poll) {
const chartCanvas = document.getElementById('results-chart');
if (!poll) {
pollResultsContainer.innerHTML = `
Start a poll to see live results.
`;
chartCanvas.classList.add('hidden');
downloadPdfBtn.disabled = true;
return;
}
chartCanvas.classList.remove('hidden');
downloadPdfBtn.disabled = false;
const ctx = chartCanvas.getContext('2d');
const data = {
labels: poll.options,
datasets: [{
label: 'Votes',
data: poll.options.map(opt => poll.votes[opt] || 0),
backgroundColor: ['#60A5FA', '#34D399', '#FBBF24', '#A78BFA', '#F87171', '#fb923c'],
borderColor: '#fff',
borderWidth: 1
}]
};
if (resultsChart) resultsChart.destroy();
resultsChart = new Chart(ctx, { type: 'bar', data: data, options: { indexAxis: 'y', responsive: true, plugins: { legend: { display: false } } } });
}
function renderQuestionList(questions) {
if (questions.length === 0) {
questionList.innerHTML = `
No questions have been asked yet.
`;
return;
}
questionList.innerHTML = questions.map(q => `
${q.text}
`).join('');
}
// --- FIRESTORE ACTIONS ---
async function createPoll(e) {
e.preventDefault();
const question = document.getElementById('poll-question').value;
const options = Array.from(document.querySelectorAll('input[name="poll-option"]')).map(el => el.value.trim()).filter(Boolean);
if (question && options.length > 1) {
const votes = options.reduce((acc, opt) => ({ ...acc, [opt]: 0 }), {});
await addDoc(collection(db, `/artifacts/${appId}/public/data/polls`), {
question, options, votes, isActive: false, timestamp: serverTimestamp()
});
createPollForm.reset();
document.getElementById('poll-options-container').innerHTML = `
`;
}
}
async function startPoll(pollId) {
const pollsRef = collection(db, `/artifacts/${appId}/public/data/polls`);
const batch = writeBatch(db);
const snapshot = await getDocs(pollsRef);
snapshot.forEach(doc => {
batch.update(doc.ref, { isActive: doc.id === pollId });
});
await batch.commit();
}
async function submitVote(pollId, option) {
if (localStorage.getItem(`voted_${pollId}_${currentUserId}`)) return;
const pollRef = doc(db, `/artifacts/${appId}/public/data/polls`, pollId);
await updateDoc(pollRef, { [`votes.${option}`]: increment(1) });
localStorage.setItem(`voted_${pollId}_${currentUserId}`, 'true');
}
async function askQuestion(e) {
e.preventDefault();
const input = document.getElementById('question-input');
const text = input.value.trim();
if (text) {
await addDoc(collection(db, `/artifacts/${appId}/public/data/questions`), {
text, submitterId: currentUserId, isAnswered: false, timestamp: serverTimestamp()
});
input.value = '';
}
}
async function toggleAnswered(questionId, isAnswered) {
const questionRef = doc(db, `/artifacts/${appId}/public/data/questions`, questionId);
await updateDoc(questionRef, { isAnswered: !isAnswered });
}
// --- EVENT HANDLERS & HELPERS ---
addOptionBtn.addEventListener('click', () => {
const container = document.getElementById('poll-options-container');
const newOption = document.createElement('input');
newOption.type = 'text';
newOption.name = 'poll-option';
newOption.placeholder = `Option ${container.children.length + 1}`;
newOption.className = 'w-full p-2 border rounded-md';
container.appendChild(newOption);
});
function generatePdf() {
const { jsPDF } = window.jspdf;
const pdfContent = document.getElementById('pdf-content');
const pdfTitleEl = document.getElementById('pdf-title');
if (!pdfContent || downloadPdfBtn.disabled) return;
pdfTitleEl.textContent = 'Live Poll Results Summary';
html2canvas(pdfContent, { scale: 2, useCORS: true, logging: false })
.then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const ratio = canvas.width / canvas.height;
const imgHeight = (pdfWidth - 20) / ratio;
pdf.addImage(imgData, 'PNG', 10, 10, pdfWidth - 20, imgHeight);
pdf.save('Poll-Results.pdf');
pdfTitleEl.textContent = '';
});
}
function addEventListeners() {
tabButtons.forEach(button => {
button.addEventListener('click', () => {
const tab = button.dataset.tab;
tabButtons.forEach(btn => btn.classList.toggle('active', btn.dataset.tab === tab));
tabContents.forEach(content => content.classList.toggle('active', content.id.startsWith(tab)));
});
});
viewToggles.forEach(toggle => {
toggle.addEventListener('change', () => {
audienceView.classList.toggle('hidden', toggle.id === 'presenter-view-toggle');
presenterView.classList.toggle('hidden', toggle.id === 'audience-view-toggle');
});
});
createPollForm.addEventListener('submit', createPoll);
pollList.addEventListener('click', e => {
if (e.target.classList.contains('start-poll-btn')) startPoll(e.target.dataset.id);
});
activePollContainer.addEventListener('click', e => {
if (e.target.classList.contains('vote-btn')) submitVote(e.target.dataset.pollId, e.target.dataset.option);
});
askQuestionForm.addEventListener('submit', askQuestion);
questionList.addEventListener('click', e => {
if (e.target.classList.contains('toggle-answered-btn')) {
toggleAnswered(e.target.dataset.id, e.target.dataset.answered === 'true');
}
});
downloadPdfBtn.addEventListener('click', generatePdf);
}
initialize();