// /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'
});
}
}