`;
list.innerHTML += item;
});
},
renderNewMeetingForm() {
const container = this.elements.attendeesContainer;
if (!container) return;
container.innerHTML = '';
this.state.teamMembers.forEach(member => {
const checkbox = `
`;
container.innerHTML += checkbox;
});
},
renderMembersTable() {
const tableBody = this.elements.membersTableBody;
if (!tableBody) return;
tableBody.innerHTML = '';
this.state.teamMembers.forEach(member => {
const row = `
${member.name}
${member.email}
`;
tableBody.innerHTML += row;
});
},
// --- EVENT HANDLERS & ACTIONS ---
changeTab(tabIndex) {
if (tabIndex < 0 || tabIndex >= this.elements.tabButtons.length) return;
this.state.currentTab = tabIndex;
this.elements.tabButtons.forEach((btn, i) => btn.classList.toggle('active', i === tabIndex));
this.elements.tabContents.forEach((content, i) => content.classList.toggle('active', i === tabIndex));
this.updateNavButtons();
},
updateNavButtons() {
if (!this.elements.prevTabBtn || !this.elements.nextTabBtn) return;
this.elements.prevTabBtn.style.visibility = this.state.currentTab === 0 ? 'hidden' : 'visible';
this.elements.nextTabBtn.style.visibility = this.state.currentTab === this.elements.tabButtons.length - 1 ? 'hidden' : 'visible';
},
addTimeSlot() {
const container = this.elements.timeSlotsContainer;
if (container) {
container.insertAdjacentHTML('beforeend', `
`);
}
},
handleNewMeetingSubmit(e) {
e.preventDefault();
const form = this.elements.newMeetingForm;
const title = form.querySelector('#meeting-title').value;
const description = form.querySelector('#meeting-description').value;
const proposedTimes = Array.from(form.querySelectorAll('.time-slot-input'))
.map(input => input.value)
.filter(value => value)
.map(time => ({ time, available: [] }));
const attendees = Array.from(form.querySelectorAll('input[name="attendees"]:checked'))
.map(cb => parseInt(cb.value));
if (!title || proposedTimes.length === 0 || attendees.length === 0) {
this.showNotification('Please fill in title, at least one time, and select attendees.', 'error');
return;
}
const newMeeting = {
id: this.getNextId(this.state.meetings),
title,
description,
attendees,
confirmedTime: null,
proposedTimes,
agenda: []
};
this.state.meetings.push(newMeeting);
this.showNotification('Meeting poll created successfully!', 'success');
form.reset();
this.renderAll();
this.changeTab(0);
},
openMeetingModal(meetingId) {
const meeting = this.state.meetings.find(m => m.id === meetingId);
if (!meeting) return;
let bodyHtml = `
`;
this.elements.modalBody.innerHTML = bodyHtml;
this.elements.modal.style.display = 'flex';
// Attach event listeners for modal actions
this.elements.modalBody.querySelectorAll('.availability-cb').forEach(cb => {
cb.addEventListener('change', (e) => this.handleAvailabilityChange(e, meetingId));
});
this.elements.modalBody.querySelector('#confirm-time-btn')?.addEventListener('click', () => this.confirmMeetingTime(meetingId));
this.elements.modalBody.querySelector('#add-agenda-form')?.addEventListener('submit', (e) => this.addAgendaItem(e, meetingId));
},
closeMeetingModal() {
this.elements.modal.style.display = 'none';
},
generatePollingHtml(meeting) {
let pollingHtml = '
`;
});
pollingHtml += ``;
return pollingHtml;
},
generateAgendaHtml(meeting) {
let agendaHtml = `
${meeting.title}
${meeting.description}
`; if (meeting.confirmedTime) { bodyHtml += `Confirmed Time
${new Date(meeting.confirmedTime).toLocaleString()}
`; } else { bodyHtml += this.generatePollingHtml(meeting); } bodyHtml += this.generateAgendaHtml(meeting); bodyHtml += `Vote on Meeting Times
'; const totalAttendees = meeting.attendees.length; let bestOptionIndex = -1; let maxAvailable = -1; meeting.proposedTimes.forEach((slot, index) => { if (slot.available.length > maxAvailable) { maxAvailable = slot.available.length; bestOptionIndex = index; } }); meeting.proposedTimes.forEach((slot, index) => { const isAvailable = slot.available.includes(this.state.currentUser.id); const availableCount = slot.available.length; const isBest = index === bestOptionIndex && availableCount > 0; pollingHtml += `
${availableCount}/${totalAttendees} Available
Agenda / Tasks
`; if (meeting.agenda.length > 0) { agendaHtml += '- ' + meeting.agenda.map(item => `
- ${item} `).join('') + '
No agenda items yet.
'; } agendaHtml += ` `; return agendaHtml; }, handleAvailabilityChange(e, meetingId) { const { checked, dataset } = e.target; const time = dataset.time; const meeting = this.state.meetings.find(m => m.id === meetingId); const slot = meeting.proposedTimes.find(s => s.time === time); if (checked) { if (!slot.available.includes(this.state.currentUser.id)) { slot.available.push(this.state.currentUser.id); } } else { slot.available = slot.available.filter(id => id !== this.state.currentUser.id); } this.openMeetingModal(meetingId); // Re-render modal }, confirmMeetingTime(meetingId) { const meeting = this.state.meetings.find(m => m.id === meetingId); let bestTime = null; let maxAvailable = -1; meeting.proposedTimes.forEach(slot => { if (slot.available.length > maxAvailable) { maxAvailable = slot.available.length; bestTime = slot.time; } }); if (bestTime) { meeting.confirmedTime = bestTime; meeting.proposedTimes = []; this.showNotification('Meeting time confirmed!', 'success'); this.closeMeetingModal(); this.renderAll(); } }, addAgendaItem(e, meetingId) { e.preventDefault(); const input = this.elements.modalBody.querySelector('#new-agenda-item'); const newItem = input.value.trim(); if (newItem) { const meeting = this.state.meetings.find(m => m.id === meetingId); meeting.agenda.push(newItem); input.value = ''; this.openMeetingModal(meetingId); // Re-render modal } }, // --- Data Config Handlers --- handleMemberFormSubmit(e) { e.preventDefault(); const form = this.elements.memberForm; const id = parseInt(this.elements.editId.value); const memberData = { name: form.querySelector('#member-name').value, email: form.querySelector('#member-email').value, }; if (id) { const index = this.state.teamMembers.findIndex(m => m.id === id); if (index > -1) { this.state.teamMembers[index] = { ...this.state.teamMembers[index], ...memberData }; this.showNotification('Member updated!', 'success'); } } else { memberData.id = this.getNextId(this.state.teamMembers); this.state.teamMembers.push(memberData); this.showNotification('Member added!', 'success'); } this.clearMemberForm(); this.renderAll(); }, clearMemberForm() { this.elements.memberForm.reset(); this.elements.editId.value = ''; }, handleMemberTableActions(e) { const target = e.target.closest('button'); if (!target) return; const action = target.dataset.action; const id = parseInt(target.dataset.id); if (action === 'edit') { const member = this.state.teamMembers.find(m => m.id === id); if (member) { const form = this.elements.memberForm; this.elements.editId.value = member.id; form.querySelector('#member-name').value = member.name; form.querySelector('#member-email').value = member.email; form.scrollIntoView({ behavior: 'smooth' }); } } else if (action === 'delete') { if (confirm('Are you sure?')) { this.state.teamMembers = this.state.teamMembers.filter(m => m.id !== id); this.showNotification('Member deleted.', 'success'); this.renderAll(); } } }, downloadMeetingPDF(meetingId) { if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { this.showNotification('PDF generation is currently unavailable.', 'error'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF(); const meeting = this.state.meetings.find(m => m.id === meetingId); if (!meeting) return; doc.setFontSize(18); doc.text(`Meeting Summary: ${meeting.title}`, 105, 22, { align: 'center' }); doc.setFontSize(11); doc.setTextColor(100); doc.text(meeting.description, 14, 35); const status = meeting.confirmedTime ? `Confirmed for: ${new Date(meeting.confirmedTime).toLocaleString()}` : 'Status: Polling for availability'; doc.setFontSize(12); doc.setTextColor(0); doc.text(status, 14, 45); const attendees = meeting.attendees.map(id => this.state.teamMembers.find(m => m.id === id)?.name).filter(Boolean); doc.autoTable({ startY: 55, head: [['Attendees']], body: attendees.map(name => [name]), theme: 'striped', headStyles: { fillColor: [8, 145, 178] } }); if (meeting.agenda.length > 0) { doc.autoTable({ startY: doc.autoTable.previous.finalY + 10, head: [['Agenda / Tasks']], body: meeting.agenda.map(item => [item]), theme: 'striped', headStyles: { fillColor: [99, 102, 241] } }); } doc.save(`Meeting_${meeting.title.replace(/\s/g, '_')}.pdf`); this.showNotification('PDF download started.', 'success'); }, // --- UTILITY FUNCTIONS --- getNextId(array) { return array.length > 0 ? Math.max(...array.map(item => item.id)) + 1 : 1; }, showNotification(message, type = 'success') { const box = this.elements.notificationBox; if (!box) return; box.textContent = message; box.className = `smcs-notification ${type}`; box.classList.add('show'); setTimeout(() => { box.classList.remove('show'); }, 3000); } }; // Make SMCS object globally accessible for inline onclick handlers window.SMCS = SMCS; // Start the application SMCS.init(); });