// /public/js/calendar/render.js function renderCalendar() { updatePeriodTitle(); if (currentView === 'month') { renderMonthView(); } else if (currentView === 'week') { renderWeekView(); } else if (currentView === 'day') { renderDayView(); } } function renderMonthView() { const container = document.getElementById('calendarView'); if (!container) return; container.innerHTML = ''; const calendarGrid = document.createElement('div'); calendarGrid.className = 'calendar-grid'; calendarGrid.style.display = 'grid'; calendarGrid.style.gridTemplateColumns = 'repeat(7, 1fr)'; calendarGrid.style.borderCollapse = 'collapse'; calendarGrid.style.width = '100%'; calendarGrid.style.height = '100%'; container.appendChild(calendarGrid); // Start with Monday const daysOfWeek = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; daysOfWeek.forEach(day => { const header = document.createElement('div'); header.className = 'calendar-day-header'; header.innerHTML = `${day}${day.substring(0, 3)}`; calendarGrid.appendChild(header); }); const firstDay = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1); const lastDay = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0); const startDate = new Date(firstDay); // If it's Sunday (0), we need to go back 6 days. If it's Monday (1), we go back 0 days. const dayOfWeek = firstDay.getDay(); const diffToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1; startDate.setDate(startDate.getDate() - diffToMonday); const endDate = new Date(lastDay); const lastDayOfWeek = lastDay.getDay(); // If it's Sunday (0), add 0. If it's Saturday (6), add 1. const daysUntilSunday = lastDayOfWeek === 0 ? 0 : 7 - lastDayOfWeek; endDate.setDate(endDate.getDate() + daysUntilSunday); const today = new Date(); today.setHours(0, 0, 0, 0); for (let date = new Date(startDate); date <= endDate; date.setDate(date.getDate() + 1)) { const dayCell = document.createElement('div'); dayCell.className = 'calendar-day'; dayCell.style.height = 'calc((100vh - 240px) / 6)'; dayCell.style.minHeight = '100px'; dayCell.style.display = 'flex'; dayCell.style.flexDirection = 'column'; dayCell.style.overflow = 'hidden'; dayCell.style.padding = '0'; dayCell.style.border = '1px solid #ddd'; dayCell.style.backgroundColor = 'white'; const isToday = date.getTime() === today.getTime(); const isOtherMonth = date.getMonth() !== currentDate.getMonth(); if (isToday) dayCell.classList.add('today'); if (isOtherMonth) dayCell.classList.add('other-month'); const dayNumber = document.createElement('div'); dayNumber.className = 'day-number'; dayNumber.textContent = date.getDate(); dayNumber.style.padding = '4px'; dayNumber.style.fontWeight = 'bold'; dayNumber.style.flexShrink = '0'; dayCell.appendChild(dayNumber); const dateStr = formatDate(date); const dayEvents = filteredEvents .filter(event => { const eventStart = event.start_date.split(' ')[0]; const eventEnd = event.end_date.split(' ')[0]; return dateStr >= eventStart && dateStr <= eventEnd; }) .sort((a, b) => { return a.start_date.localeCompare(b.start_date); }); if (dayEvents.length > 0) { const eventsContainer = document.createElement('div'); eventsContainer.className = 'day-events'; eventsContainer.style.flex = '1'; eventsContainer.style.minHeight = '0'; eventsContainer.style.overflowY = 'auto'; eventsContainer.style.overflowX = 'hidden'; eventsContainer.style.display = 'flex'; eventsContainer.style.flexDirection = 'column'; eventsContainer.style.gap = '2px'; eventsContainer.style.padding = '2px'; const eventItems = dayEvents.map(event => { const eventTime = event.all_day ? '' : ` - ${formatTime(event.start_date)}`; let attendeePills = ''; if (event.attendee_names) { const attendees = event.attendee_names.split(',').map(name => name.trim()); attendeePills = attendees.map((name, index) => { const colorClass = `attendee-color-${(index % 8) + 1}`; const initials = name.split(' ').map(n => n[0]).join('').substring(0, 2).toUpperCase(); return `${initials}`; }).join(''); } return `
${escapeHtml(event.title)}${eventTime} ${attendeePills ? `
${attendeePills}
` : ''}
`; }).join(''); eventsContainer.innerHTML = eventItems; dayCell.appendChild(eventsContainer); } calendarGrid.appendChild(dayCell); } } function renderWeekView() { const container = document.getElementById('calendarView'); if (!container) return; container.innerHTML = ''; const calendarGrid = document.createElement('div'); calendarGrid.className = 'calendar-grid'; calendarGrid.style.display = 'grid'; calendarGrid.style.gridTemplateColumns = 'repeat(7, 1fr)'; calendarGrid.style.borderCollapse = 'collapse'; calendarGrid.style.width = '100%'; calendarGrid.style.height = '100%'; container.appendChild(calendarGrid); const daysOfWeek = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; daysOfWeek.forEach(day => { const header = document.createElement('div'); header.className = 'calendar-day-header'; header.innerHTML = `${day}${day.substring(0, 3)}`; calendarGrid.appendChild(header); }); const startOfWeek = new Date(currentDate); const currentDay = currentDate.getDay(); const diffToMonday = currentDay === 0 ? 6 : currentDay - 1; startOfWeek.setDate(currentDate.getDate() - diffToMonday); startOfWeek.setHours(0,0,0,0); const today = new Date(); today.setHours(0,0,0,0); for (let i = 0; i < 7; i++) { const date = new Date(startOfWeek); date.setDate(startOfWeek.getDate() + i); const dayCell = document.createElement('div'); dayCell.className = 'calendar-day'; dayCell.style.height = 'calc(100vh - 180px)'; dayCell.style.display = 'flex'; dayCell.style.flexDirection = 'column'; dayCell.style.overflow = 'hidden'; dayCell.style.padding = '0'; dayCell.style.border = '1px solid #ddd'; dayCell.style.backgroundColor = 'white'; if (date.getTime() === today.getTime()) dayCell.classList.add('today'); const dayNumber = document.createElement('div'); dayNumber.className = 'day-number'; dayNumber.textContent = `${date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}`; dayNumber.style.padding = '8px'; dayNumber.style.fontWeight = 'bold'; dayNumber.style.borderBottom = '1px solid #eee'; dayNumber.style.backgroundColor = '#f9fafb'; dayCell.appendChild(dayNumber); const dateStr = formatDate(date); const dayEvents = filteredEvents .filter(event => { const eventStart = event.start_date.split(' ')[0]; const eventEnd = event.end_date.split(' ')[0]; return dateStr >= eventStart && dateStr <= eventEnd; }) .sort((a, b) => { return a.start_date.localeCompare(b.start_date); }); if (dayEvents.length > 0) { const eventsContainer = document.createElement('div'); eventsContainer.style.flex = '1'; eventsContainer.style.overflowY = 'auto'; eventsContainer.style.padding = '4px'; eventsContainer.style.display = 'flex'; eventsContainer.style.flexDirection = 'column'; eventsContainer.style.gap = '4px'; const eventItems = dayEvents.map(event => { const eventTime = event.all_day ? 'All Day' : formatTime(event.start_date); return `
${escapeHtml(event.title)}
${eventTime}
`; }).join(''); eventsContainer.innerHTML = eventItems; dayCell.appendChild(eventsContainer); } calendarGrid.appendChild(dayCell); } } function renderDayView() { const container = document.getElementById('calendarView'); if (!container) return; container.innerHTML = ''; const dayContainer = document.createElement('div'); dayContainer.style.height = 'calc(100vh - 150px)'; dayContainer.style.overflowY = 'auto'; dayContainer.style.backgroundColor = 'white'; dayContainer.style.borderRadius = '8px'; dayContainer.style.border = '1px solid #ddd'; dayContainer.style.display = 'flex'; dayContainer.style.flexDirection = 'column'; container.appendChild(dayContainer); const dateStr = formatDate(currentDate); for (let hour = 0; hour < 24; hour++) { const hourRow = document.createElement('div'); hourRow.style.display = 'flex'; hourRow.style.borderBottom = '1px solid #f0f0f0'; hourRow.style.minHeight = '60px'; const timeLabel = document.createElement('div'); timeLabel.style.width = '80px'; timeLabel.style.padding = '10px'; timeLabel.style.textAlign = 'right'; timeLabel.style.color = '#666'; timeLabel.style.fontSize = '0.85em'; timeLabel.style.borderRight = '1px solid #f0f0f0'; timeLabel.style.fontWeight = 'bold'; const displayHour = hour === 0 ? '12 AM' : (hour > 12 ? `${hour - 12} PM` : (hour === 12 ? '12 PM' : `${hour} AM`)); timeLabel.textContent = displayHour; const eventsCell = document.createElement('div'); eventsCell.style.flex = '1'; eventsCell.style.padding = '4px'; eventsCell.style.position = 'relative'; const hourEvents = filteredEvents.filter(event => { const eventStart = event.start_date.split(' ')[0]; const eventEnd = event.end_date.split(' ')[0]; if (dateStr < eventStart || dateStr > eventEnd) return false; if (event.all_day && hour === 0) return true; if (event.all_day) return false; const timePart = event.start_date.split(' ')[1]; const eventHour = parseInt(timePart.split(':')[0]); return eventHour === hour; }); if (hourEvents.length > 0) { eventsCell.innerHTML = hourEvents.map(event => { return `
${escapeHtml(event.title)} ${event.all_day ? '(All Day)' : ''}
`; }).join(''); } hourRow.appendChild(timeLabel); hourRow.appendChild(eventsCell); dayContainer.appendChild(hourRow); } } function updatePeriodTitle() { const titleElement = document.getElementById('currentPeriod'); if (!titleElement) return; if (currentView === 'month') { const monthName = currentDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' }); titleElement.textContent = monthName; } else if (currentView === 'week') { const startOfWeek = new Date(currentDate); const currentDay = currentDate.getDay(); const diffToMonday = currentDay === 0 ? 6 : currentDay - 1; startOfWeek.setDate(currentDate.getDate() - diffToMonday); const endOfWeek = new Date(startOfWeek); endOfWeek.setDate(endOfWeek.getDate() + 6); const startStr = startOfWeek.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const endStr = endOfWeek.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); titleElement.textContent = `${startStr} - ${endStr}`; } else if (currentView === 'day') { titleElement.textContent = currentDate.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } }