// Global variables let currentDateRange = 'this-week'; let selectedCells = new Set(); let isCommandPanelOpen = false; let teamMembers = [ { id: 'alice', name: 'Alice Smith', avatar: 'http://static.photos/people/200x200/1', capacity: 8, role: 'Frontend Developer', status: 'available' }, { id: 'bob', name: 'Bob Wilson', avatar: 'http://static.photos/people/200x200/2', capacity: 6, role: 'Backend Developer', status: 'available' }, { id: 'charlie', name: 'Charlie Brown', avatar: 'http://static.photos/people/200x200/3', capacity: 8, role: 'QA Engineer', status: 'available' }, { id: 'diana', name: 'Diana Prince', avatar: 'http://static.photos/people/200x200/4', capacity: 7, role: 'Product Manager', status: 'available' } ]; // Column management let columnOrder = teamMembers.map(member => member.id); let columnWidths = {}; teamMembers.forEach(member => { columnWidths[member.id] = 200; // Default width }); let tasks = [ { id: 1, title: 'API Integration', assignee: 'alice', date: getDateString(new Date()), status: 'in-progress', priority: 'high', timeEstimate: 4, links: 2 }, { id: 2, title: 'Database Migration', assignee: 'bob', date: getDateString(new Date()), status: 'not-started', priority: 'medium', timeEstimate: 6, links: 0 }, { id: 3, title: 'Frontend Refactor', assignee: 'charlie', date: getDateString(new Date()), status: 'complete', priority: 'low', timeEstimate: 8, links: 1 }, { id: 4, title: 'Security Audit', assignee: 'diana', date: getDateString(new Date()), status: 'blocked', priority: 'high', timeEstimate: 3, links: 3 } ]; // Sprint boundaries let sprintBoundaries = [ { name: 'Sprint 1', start: new Date(), end: new Date(new Date().setDate(new Date().getDate() + 14)) } ]; // Utility functions function getDateString(date) { return date.toISOString().split('T')[0]; } function formatDateDisplay(date) { const options = { weekday: 'short', month: 'numeric', day: 'numeric' }; return date.toLocaleDateString('en-US', options); } function getDateRange(rangeType) { const today = new Date(); const start = new Date(today); const end = new Date(today); switch(rangeType) { case 'this-week': start.setDate(today.getDate() - today.getDay() + 1); end.setDate(start.getDate() + 4); break; case 'next-2-weeks': start.setDate(today.getDate() - today.getDay() + 1); end.setDate(start.getDate() + 9); break; case 'this-quarter': const quarter = Math.floor(today.getMonth() / 3); start.setMonth(quarter * 3, 1); end.setMonth((quarter + 1) * 3, 0); break; default: start.setDate(today.getDate() - today.getDay() + 1); end.setDate(start.getDate() + 4); } return { start, end }; } function generateDates(startDate, endDate) { const dates = []; const current = new Date(startDate); while (current <= endDate) { dates.push(new Date(current)); current.setDate(current.getDate() + 1); } return dates; } // Calendar initialization function initializeCalendar() { const calendarGrid = document.getElementById('calendar-grid'); const { start, end } = getDateRange(currentDateRange); const dates = generateDates(start, end); // Create calendar structure let calendarHTML = `
Date
`; // Team Member Column Headers teamMembers.forEach(member => { calendarHTML += `
${member.name} ${member.id}
`; }); // Milestone Column Header calendarHTML += `
Milestones
`; // Date Rows dates.forEach((date, index) => { const dateStr = getDateString(date); const isWeekend = date.getDay() === 0 || date.getDay() === 6; const isSprintBoundary = sprintBoundaries.some(sprint => getDateString(sprint.start) === dateStr || getDateString(sprint.end) === dateStr ); // Add sprint boundary class if needed const rowClasses = isSprintBoundary ? 'sprint-boundary' : ''; calendarHTML += `
${formatDateDisplay(date)}
`; // Team Member Cells teamMembers.forEach(member => { const cellTasks = tasks.filter(task => task.assignee === member.id && task.date === dateStr ); calendarHTML += `
${renderCellContent(cellTasks)}
`; }); // Milestone Cell calendarHTML += `
`; }); calendarGrid.innerHTML = calendarHTML; feather.replace(); } function renderCellContent(cellTasks) { if (cellTasks.length === 0) return ''; let content = ''; const visibleTasks = cellTasks.slice(0, 3); const hiddenCount = cellTasks.length - 3; visibleTasks.forEach(task => { content += `
${task.title}
${task.timeEstimate}h ${task.links > 0 ? `🔗 ${task.links}` : ''}
`; }); if (hiddenCount > 0) { content += `
+${hiddenCount} more
`; } return content; } // Event Handlers function handleCellClick(event, date, assignee) { event.stopPropagation(); const cell = event.currentTarget; const cellKey = `${date}-${assignee}`; if (event.ctrlKey || event.metaKey) { // Multi-select if (selectedCells.has(cellKey)) { selectedCells.delete(cellKey); cell.classList.remove('cell-selected'); } else { selectedCells.add(cellKey); cell.classList.add('cell-selected'); } } else if (event.shiftKey && selectedCells.size > 0) { // Range select // Implementation for range selection } else { // Single select clearCellSelection(); selectedCells.add(cellKey); cell.classList.add('cell-selected'); } } function clearCellSelection() { document.querySelectorAll('.cell').forEach(cell => { cell.classList.remove('cell-selected'); }); selectedCells.clear(); } function handleDragStart(event, taskId) { event.dataTransfer.setData('text/plain', taskId.toString()); event.currentTarget.classList.add('dragging'); } function handleDragOver(event) { event.preventDefault(); event.currentTarget.classList.add('drag-over-valid'); } function handleDrop(event, date, assignee) { event.preventDefault(); event.currentTarget.classList.remove('drag-over-valid', 'dragging'); const taskId = parseInt(event.dataTransfer.getData('text/plain')); const task = tasks.find(t => t.id === taskId); if (task) { // Update task assignment and date task.assignee = assignee; task.date = date; initializeCalendar(); // Refresh calendar } } // Quick Add Button document.getElementById('quick-add-btn')?.addEventListener('click', function() { // Open command panel with task creation template const commandPanel = document.querySelector('custom-command-panel'); if (commandPanel && commandPanel.openPanel) { commandPanel.openPanel('task-create'); } }); // Keyboard shortcuts document.addEventListener('keydown', function(event) { // Ctrl+K or Cmd+K to open command panel if ((event.ctrlKey || event.metaKey) && event.key === 'k') { event.preventDefault(); const commandPanel = document.querySelector('custom-command-panel'); if (commandPanel && commandPanel.togglePanel) { commandPanel.togglePanel(); } } // Escape to clear selection if (event.key === 'Escape') { clearCellSelection(); } }); // Export functions for web components window.CalendarUtils = { getDateString, formatDateDisplay, getDateRange, generateDates, teamMembers, tasks, selectedCells, clearCellSelection };