// 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 += `
`;
});
// 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.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
};