deepresearch / script.js
GraziePrego's picture
Upload folder using huggingface_hub
d7790fd verified
// Data
const teamMembers = [
{ id: 1, name: 'Alex Morgan', role: 'Product Owner', avatar: 'http://static.photos/people/200x200/42' },
{ id: 2, name: 'Sarah Chen', role: 'UI Designer', avatar: 'http://static.photos/people/200x200/45' },
{ id: 3, name: 'Mike Johnson', role: 'Frontend Dev', avatar: 'http://static.photos/people/200x200/48' },
{ id: 4, name: 'Emma Wilson', role: 'UX Researcher', avatar: 'http://static.photos/people/200x200/52' }
];
// Sprint Planning Data
let projectGoals = [
{ id: 1, title: 'Complete Design System v2', description: 'Finalize all components and documentation', priority: 'high' },
{ id: 2, title: 'Mobile App MVP', description: 'Launch iOS and Android MVP versions', priority: 'high' },
{ id: 3, title: 'Performance Optimization', description: 'Improve load times by 50%', priority: 'medium' }
];
let backlogTasks = [
{ id: 101, title: 'Design button component library', points: 5, goalId: 1, type: 'design', status: 'backlog' },
{ id: 102, title: 'Create icon system', points: 3, goalId: 1, type: 'design', status: 'backlog' },
{ id: 103, title: 'Setup React Native project', points: 8, goalId: 2, type: 'dev', status: 'backlog' },
{ id: 104, title: 'Implement navigation', points: 5, goalId: 2, type: 'dev', status: 'backlog' },
{ id: 105, title: 'User authentication flow', points: 8, goalId: 2, type: 'dev', status: 'backlog' },
{ id: 106, title: 'Optimize image loading', points: 5, goalId: 3, type: 'dev', status: 'backlog' },
{ id: 107, title: 'Database query optimization', points: 8, goalId: 3, type: 'dev', status: 'backlog' },
{ id: 108, title: 'Research competitor apps', points: 3, goalId: 2, type: 'research', status: 'backlog' },
{ id: 109, title: 'Write documentation', points: 3, goalId: 1, type: 'dev', status: 'backlog' },
{ id: 110, title: 'Setup CI/CD pipeline', points: 5, goalId: 2, type: 'dev', status: 'backlog' }
];
let generatedSprintsData = [];
let tasks = [
{ id: 1, title: 'Design system documentation', type: 'design', priority: 'high', status: 'todo', assignee: 2, tags: ['Design', 'Docs'], comments: 3 },
{ id: 2, title: 'Fix navigation responsiveness', type: 'bug', priority: 'high', status: 'progress', assignee: 3, tags: ['Bug', 'Mobile'], comments: 1 },
{ id: 3, title: 'User interview synthesis', type: 'research', priority: 'medium', status: 'review', assignee: 4, tags: ['Research'], comments: 5 },
{ id: 4, title: 'API integration dashboard', type: 'dev', priority: 'high', status: 'progress', assignee: 3, tags: ['Backend'], comments: 2 },
{ id: 5, title: 'Color palette refinement', type: 'design', priority: 'low', status: 'done', assignee: 2, tags: ['Design'], comments: 0 },
{ id: 6, title: 'Accessibility audit', type: 'research', priority: 'medium', status: 'todo', assignee: 4, tags: ['A11y'], comments: 4 },
{ id: 7, title: 'Component library setup', type: 'dev', priority: 'high', status: 'done', assignee: 3, tags: ['Frontend'], comments: 8 },
{ id: 8, title: 'Landing page animations', type: 'design', priority: 'medium', status: 'review', assignee: 2, tags: ['Animation'], comments: 2 },
{ id: 9, title: 'Database schema design', type: 'dev', priority: 'high', status: 'todo', assignee: 3, tags: ['Backend'], comments: 1 },
{ id: 10, title: 'Mobile app wireframes', type: 'design', priority: 'medium', status: 'done', assignee: 2, tags: ['Mobile'], comments: 3 },
{ id: 11, title: 'Performance optimization', type: 'dev', priority: 'low', status: 'done', assignee: 3, tags: ['Performance'], comments: 0 }
];
const activities = [
{ user: 2, action: 'moved', target: 'Design system', from: 'In Progress', to: 'Review', time: '2 min ago' },
{ user: 3, action: 'completed', target: 'API Integration', time: '1 hour ago' },
{ user: 4, action: 'commented on', target: 'User Research', time: '3 hours ago' },
{ user: 1, action: 'created', target: 'Sprint 24 Planning', time: '5 hours ago' },
{ user: 2, action: 'uploaded', target: '3 new mockups', time: 'Yesterday' }
];
let selectedAssignee = null;
// Initialize
document.addEventListener('DOMContentLoaded', () => {
lucide.createIcons();
renderBoard();
renderActivity();
initChart();
renderAssignees();
initSprintPlanner();
// Mobile menu toggle
document.getElementById('mobile-menu-btn').addEventListener('click', () => {
const sidebar = document.getElementById('sidebar');
sidebar.classList.toggle('hidden');
});
// Form submission
document.getElementById('newTaskForm').addEventListener('submit', handleNewTask);
document.getElementById('goalForm').addEventListener('submit', handleNewGoal);
document.getElementById('backlogForm').addEventListener('submit', handleNewBacklogTask);
// Capacity calculation listeners
document.getElementById('teamVelocity')?.addEventListener('input', updateCapacity);
document.getElementById('bufferPercent')?.addEventListener('input', updateCapacity);
});
// View Navigation
function showSprintPlanner() {
document.querySelector('main:not(#sprintPlannerView)').classList.add('hidden');
document.getElementById('sprintPlannerView').classList.remove('hidden');
document.getElementById('sprintPlannerView').classList.add('block');
renderGoals();
renderBacklog();
updateCapacity();
lucide.createIcons();
}
function showBoard() {
document.querySelector('main:not(#sprintPlannerView)').classList.remove('hidden');
document.getElementById('sprintPlannerView').classList.add('hidden');
document.getElementById('sprintPlannerView').classList.remove('block');
lucide.createIcons();
}
// Sprint Planning Functions
function initSprintPlanner() {
updateGoalSelect();
}
function updateCapacity() {
const velocity = parseInt(document.getElementById('teamVelocity')?.value || 40);
const buffer = parseInt(document.getElementById('bufferPercent')?.value || 20);
const effective = Math.floor(velocity * (1 - buffer / 100));
document.getElementById('effectiveCapacity').textContent = effective + ' pts';
const totalPoints = backlogTasks.reduce((sum, task) => sum + task.points, 0);
document.getElementById('totalBacklogPoints').textContent = totalPoints + ' pts';
}
function renderGoals() {
const container = document.getElementById('goalsList');
if (!container) return;
container.innerHTML = projectGoals.map(goal => `
<div class="flex items-start gap-3 p-3 bg-gray-50 rounded-xl border border-gray-100">
<div class="w-2 h-2 rounded-full mt-2 ${getPriorityDot(goal.priority)}"></div>
<div class="flex-1">
<h4 class="font-semibold text-gray-900 text-sm">${goal.title}</h4>
<p class="text-xs text-gray-500 mt-1">${goal.description}</p>
<div class="flex items-center gap-2 mt-2">
<span class="text-xs px-2 py-0.5 bg-white rounded border border-gray-200 text-gray-600">
${backlogTasks.filter(t => t.goalId === goal.id).reduce((s, t) => s + t.points, 0)} pts
</span>
<span class="text-xs text-gray-400">${backlogTasks.filter(t => t.goalId === goal.id).length} tasks</span>
</div>
</div>
<button onclick="deleteGoal(${goal.id})" class="text-gray-400 hover:text-red-500">
<i data-lucide="trash-2" class="w-4 h-4"></i>
</button>
</div>
`).join('');
lucide.createIcons();
}
function renderBacklog() {
const container = document.getElementById('backlogTasks');
if (!container) return;
container.innerHTML = backlogTasks.map(task => {
const goal = projectGoals.find(g => g.id === task.goalId);
return `
<div class="flex items-center gap-3 p-3 bg-white rounded-xl border border-gray-200 hover:shadow-md transition-shadow">
<div class="w-8 h-8 rounded-lg ${getTypeColor(task.type)} flex items-center justify-center flex-shrink-0">
<i data-lucide="${getTypeIcon(task.type)}" class="w-4 h-4"></i>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-900 truncate">${task.title}</p>
<div class="flex items-center gap-2 mt-0.5">
<span class="text-xs font-semibold text-indigo-600 bg-indigo-50 px-1.5 py-0.5 rounded">${task.points} pts</span>
${goal ? `<span class="text-xs text-gray-500 truncate">${goal.title}</span>` : ''}
</div>
</div>
<button onclick="deleteBacklogTask(${task.id})" class="text-gray-400 hover:text-red-500 flex-shrink-0">
<i data-lucide="x" class="w-4 h-4"></i>
</button>
</div>
`}).join('');
lucide.createIcons();
updateCapacity();
updateGoalSelect();
}
function getPriorityDot(priority) {
const colors = { high: 'bg-red-500', medium: 'bg-orange-500', low: 'bg-green-500' };
return colors[priority] || 'bg-gray-500';
}
function getTypeColor(type) {
const colors = {
design: 'bg-pink-100 text-pink-600',
dev: 'bg-blue-100 text-blue-600',
research: 'bg-purple-100 text-purple-600',
testing: 'bg-green-100 text-green-600'
};
return colors[type] || 'bg-gray-100 text-gray-600';
}
function updateGoalSelect() {
const select = document.getElementById('backlogGoal');
if (!select) return;
select.innerHTML = '<option value="">No Goal</option>' +
projectGoals.map(g => `<option value="${g.id}">${g.title}</option>`).join('');
}
// Modals
function addGoal() {
document.getElementById('goalModal').classList.remove('hidden');
}
function closeGoalModal() {
document.getElementById('goalModal').classList.add('hidden');
document.getElementById('goalForm').reset();
}
function addBacklogTask() {
document.getElementById('backlogModal').classList.remove('hidden');
}
function closeBacklogModal() {
document.getElementById('backlogModal').classList.add('hidden');
document.getElementById('backlogForm').reset();
}
function handleNewGoal(e) {
e.preventDefault();
const newGoal = {
id: Date.now(),
title: document.getElementById('goalTitle').value,
description: document.getElementById('goalDesc').value,
priority: document.getElementById('goalPriority').value
};
projectGoals.push(newGoal);
renderGoals();
closeGoalModal();
updateGoalSelect();
}
function handleNewBacklogTask(e) {
e.preventDefault();
const newTask = {
id: Date.now(),
title: document.getElementById('backlogTitle').value,
points: parseInt(document.getElementById('backlogPoints').value),
goalId: parseInt(document.getElementById('backlogGoal').value) || null,
type: document.getElementById('backlogType').value,
status: 'backlog'
};
backlogTasks.push(newTask);
renderBacklog();
closeBacklogModal();
}
function deleteGoal(id) {
projectGoals = projectGoals.filter(g => g.id !== id);
backlogTasks = backlogTasks.filter(t => t.goalId !== id);
renderGoals();
renderBacklog();
}
function deleteBacklogTask(id) {
backlogTasks = backlogTasks.filter(t => t.id !== id);
renderBacklog();
}
// Sprint Generation Algorithm
function generateSprints() {
const velocity = parseInt(document.getElementById('teamVelocity').value);
const buffer = parseInt(document.getElementById('bufferPercent').value);
const effectiveCapacity = Math.floor(velocity * (1 - buffer / 100));
const duration = parseInt(document.getElementById('sprintDuration').value);
// Sort tasks by priority (high priority goals first) and points
const sortedTasks = [...backlogTasks].sort((a, b) => {
const goalA = projectGoals.find(g => g.id === a.goalId);
const goalB = projectGoals.find(g => g.id === b.goalId);
const priorityWeight = { high: 3, medium: 2, low: 1 };
const priorityDiff = (priorityWeight[goalB?.priority] || 0) - (priorityWeight[goalA?.priority] || 0);
if (priorityDiff !== 0) return priorityDiff;
return b.points - a.points; // Larger tasks first for better packing
});
const sprints = [];
let currentSprint = { number: 1, tasks: [], totalPoints: 0, goals: new Set() };
let unassignedTasks = [];
sortedTasks.forEach(task => {
if (currentSprint.totalPoints + task.points <= effectiveCapacity) {
currentSprint.tasks.push(task);
currentSprint.totalPoints += task.points;
if (task.goalId) currentSprint.goals.add(task.goalId);
} else {
// Check if any remaining tasks can fit
const remainingSpace = effectiveCapacity - currentSprint.totalPoints;
const fittingTask = sortedTasks.find(t =>
!currentSprint.tasks.includes(t) &&
!unassignedTasks.includes(t) &&
t.points <= remainingSpace &&
t.id !== task.id
);
if (fittingTask && fittingTask.id !== task.id) {
currentSprint.tasks.push(fittingTask);
currentSprint.totalPoints += fittingTask.points;
if (fittingTask.goalId) currentSprint.goals.add(fittingTask.goalId);
unassignedTasks.push(task);
} else {
sprints.push(currentSprint);
currentSprint = {
number: sprints.length + 1,
tasks: [task],
totalPoints: task.points,
goals: new Set(task.goalId ? [task.goalId] : [])
};
}
}
});
if (currentSprint.tasks.length > 0) {
sprints.push(currentSprint);
}
// Add unassigned tasks to new sprints if any
unassignedTasks.forEach(task => {
const lastSprint = sprints[sprints.length - 1];
if (lastSprint && lastSprint.totalPoints + task.points <= effectiveCapacity) {
lastSprint.tasks.push(task);
lastSprint.totalPoints += task.points;
if (task.goalId) lastSprint.goals.add(task.goalId);
} else {
sprints.push({
number: sprints.length + 1,
tasks: [task],
totalPoints: task.points,
goals: new Set(task.goalId ? [task.goalId] : [])
});
}
});
generatedSprintsData = sprints;
renderGeneratedSprints(sprints, effectiveCapacity, duration);
}
function renderGeneratedSprints(sprints, capacity, duration) {
const container = document.getElementById('sprintsPreview');
const wrapper = document.getElementById('generatedSprints');
if (sprints.length === 0) {
container.innerHTML = '<p class="text-gray-500 text-sm text-center py-4">No tasks to generate sprints from</p>';
} else {
container.innerHTML = sprints.map(sprint => {
const goalNames = Array.from(sprint.goals).map(gid => {
const goal = projectGoals.find(g => g.id === gid);
return goal ? goal.title : '';
}).filter(Boolean);
const utilization = Math.round((sprint.totalPoints / capacity) * 100);
const utilizationColor = utilization > 90 ? 'text-green-600 bg-green-50' :
utilization > 70 ? 'text-blue-600 bg-blue-50' : 'text-orange-600 bg-orange-50';
return `
<div class="bg-white p-4 rounded-xl border border-gray-200 shadow-sm">
<div class="flex justify-between items-start mb-3">
<div>
<h4 class="font-bold text-gray-900">Sprint ${sprint.number}</h4>
<p class="text-xs text-gray-500 mt-0.5">${duration} weeks • ${sprint.tasks.length} tasks</p>
</div>
<span class="text-xs font-semibold px-2 py-1 rounded-lg ${utilizationColor}">
${sprint.totalPoints}/${capacity} pts
</span>
</div>
<div class="space-y-2 mb-3">
${sprint.tasks.slice(0, 3).map(t => `
<div class="flex items-center gap-2 text-xs">
<span class="w-5 h-5 rounded flex items-center justify-center ${getTypeColor(t.type)} flex-shrink-0">
<i data-lucide="${getTypeIcon(t.type)}" class="w-3 h-3"></i>
</span>
<span class="text-gray-700 truncate flex-1">${t.title}</span>
<span class="text-gray-400 flex-shrink-0">${t.points}p</span>
</div>
`).join('')}
${sprint.tasks.length > 3 ? `<p class="text-xs text-gray-400 pl-7">+${sprint.tasks.length - 3} more tasks</p>` : ''}
</div>
${goalNames.length > 0 ? `
<div class="flex flex-wrap gap-1 pt-2 border-t border-gray-100">
${goalNames.map(g => `<span class="text-xs px-2 py-0.5 bg-indigo-50 text-indigo-600 rounded">${g}</span>`).join('')}
</div>
` : ''}
</div>
`;
}).join('');
}
wrapper.classList.remove('hidden');
lucide.createIcons();
}
function applyGeneratedSprints() {
if (generatedSprintsData.length === 0) return;
// Transform generated sprints into actual sprints
const startDate = new Date();
generatedSprintsData.forEach((sprint, index) => {
const sprintName = `Sprint ${25 + index}`;
const sprintTasks = sprint.tasks.map(t => ({
id: tasks.length + 1,
title: t.title,
type: t.type,
priority: 'medium',
status: index === 0 ? 'todo' : 'backlog',
assignee: teamMembers[index % teamMembers.length].id,
tags: [t.type.charAt(0).toUpperCase() + t.type.slice(1)],
comments: 0,
storyPoints: t.points,
sprintName: sprintName
}));
tasks.push(...sprintTasks);
});
// Clear backlog
backlogTasks = [];
renderBacklog();
// Show board
showBoard();
renderBoard();
// Show success message
alert(`Successfully created ${generatedSprintsData.length} sprints with ${tasks.length} tasks!`);
}
// Close modals on outside click
document.getElementById('goalModal')?.addEventListener('click', (e) => {
if (e.target === e.currentTarget) closeGoalModal();
});
document.getElementById('backlogModal')?.addEventListener('click', (e) => {
if (e.target === e.currentTarget) closeBacklogModal();
});
function getPriorityColor(priority) {
const colors = {
high: 'bg-red-100 text-red-700 border-red-200',
medium: 'bg-orange-100 text-orange-700 border-orange-200',
low: 'bg-green-100 text-green-700 border-green-200'
};
return colors[priority] || colors.medium;
}
function getTypeIcon(type) {
const icons = {
design: 'palette',
dev: 'code',
bug: 'bug',
research: 'search'
};
return icons[type] || 'circle';
}
function createTaskCard(task) {
const assignee = teamMembers.find(m => m.id === task.assignee);
const priorityClass = getPriorityColor(task.priority);
return `
<div class="task-card bg-white p-4 rounded-xl border border-gray-200 shadow-sm cursor-move"
draggable="true"
ondragstart="drag(event)"
data-task-id="${task.id}">
<div class="flex justify-between items-start mb-2">
<span class="px-2 py-1 rounded-lg text-xs font-medium border ${priorityClass} uppercase tracking-wide">
${task.priority}
</span>
<button onclick="deleteTask(${task.id})" class="text-gray-400 hover:text-red-500 transition-colors" title="Delete task">
<i data-lucide="trash-2" class="w-4 h-4"></i>
</button>
</div>
<h4 class="font-semibold text-gray-900 mb-2 line-clamp-2">${task.title}</h4>
<div class="flex items-center gap-2 mb-3">
<i data-lucide="${getTypeIcon(task.type)}" class="w-4 h-4 text-gray-400"></i>
<span class="text-xs text-gray-500 capitalize">${task.type}</span>
</div>
<div class="flex items-center justify-between pt-2 border-t border-gray-100">
<div class="flex items-center gap-2">
<img src="${assignee.avatar}" alt="${assignee.name}" class="w-6 h-6 rounded-full border border-white">
<div class="flex gap-1">
${task.tags.map(tag => `<span class="text-xs text-gray-500 bg-gray-100 px-2 py-0.5 rounded">${tag}</span>`).join('')}
</div>
</div>
<div class="flex items-center gap-1 text-gray-400 text-xs">
<i data-lucide="message-square" class="w-3 h-3"></i>
<span>${task.comments}</span>
</div>
</div>
</div>
`;
}
function deleteTask(taskId) {
if (confirm('Are you sure you want to delete this task?')) {
const taskIndex = tasks.findIndex(t => t.id === taskId);
if (taskIndex > -1) {
const deletedTask = tasks[taskIndex];
tasks.splice(taskIndex, 1);
renderBoard();
// Add to activity
activities.unshift({
user: teamMembers[0].id,
action: 'deleted',
target: deletedTask.title,
time: 'Just now'
});
renderActivity();
}
}
}
function renderBoard() {
const columns = {
todo: document.getElementById('todo-column'),
progress: document.getElementById('progress-column'),
review: document.getElementById('review-column'),
done: document.getElementById('done-column')
};
const counts = {
todo: 0,
progress: 0,
review: 0,
done: 0
};
// Clear columns
Object.values(columns).forEach(col => col.innerHTML = '');
// Populate columns
tasks.forEach(task => {
if (columns[task.status]) {
columns[task.status].innerHTML += createTaskCard(task);
counts[task.status]++;
}
});
// Update counts
document.getElementById('count-todo').textContent = counts.todo;
document.getElementById('count-progress').textContent = counts.progress;
document.getElementById('count-review').textContent = counts.review;
document.getElementById('count-done').textContent = counts.done;
lucide.createIcons();
}
function renderActivity() {
const container = document.getElementById('activity-feed');
container.innerHTML = activities.map(activity => {
const user = teamMembers.find(m => m.id === activity.user);
return `
<div class="flex gap-3 items-start">
<img src="${user.avatar}" class="w-8 h-8 rounded-full border border-gray-200 flex-shrink-0">
<div class="flex-1 min-w-0">
<p class="text-sm text-gray-900">
<span class="font-semibold">${user.name}</span>
<span class="text-gray-600">${activity.action}</span>
<span class="font-medium text-gray-900">${activity.target}</span>
</p>
<p class="text-xs text-gray-500 mt-0.5">${activity.time}</p>
</div>
</div>
`;
}).join('');
}
function renderAssignees() {
const container = document.getElementById('assigneeList');
container.innerHTML = teamMembers.map(member => `
<div class="flex-shrink-0 cursor-pointer assignee-option"
data-id="${member.id}"
onclick="selectAssignee(${member.id})">
<img src="${member.avatar}"
class="w-10 h-10 rounded-full border-2 ${selectedAssignee === member.id ? 'border-indigo-600' : 'border-transparent'} hover:scale-110 transition-transform">
<p class="text-xs text-center mt-1 text-gray-600">${member.name.split(' ')[0]}</p>
</div>
`).join('');
}
function selectAssignee(id) {
selectedAssignee = id;
renderAssignees();
}
function initChart() {
const ctx = document.getElementById('burndownChart').getContext('2d');
const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri'];
const ideal = [32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2];
const actual = [32, 30, 28, 25, 24, 20, 18, 15, 12, 8];
new Chart(ctx, {
type: 'line',
data: {
labels: days,
datasets: [
{
label: 'Ideal',
data: ideal,
borderColor: '#e5e7eb',
borderDash: [5, 5],
tension: 0.4,
pointRadius: 0,
borderWidth: 2
},
{
label: 'Actual',
data: actual,
borderColor: '#6366f1',
backgroundColor: 'rgba(99, 102, 241, 0.1)',
tension: 0.4,
fill: true,
pointBackgroundColor: '#6366f1',
pointBorderColor: '#fff',
pointBorderWidth: 2,
pointRadius: 4,
borderWidth: 3
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
position: 'top',
align: 'end',
labels: {
usePointStyle: true,
boxWidth: 8
}
}
},
scales: {
y: {
beginAtZero: true,
grid: {
color: '#f3f4f6'
},
ticks: {
color: '#9ca3af',
font: { size: 11 }
}
},
x: {
grid: {
display: false
},
ticks: {
color: '#9ca3af',
font: { size: 11 }
}
}
}
}
});
}
// Drag and Drop
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("taskId", ev.target.getAttribute('data-task-id'));
ev.target.classList.add('dragging');
}
function drop(ev) {
ev.preventDefault();
const taskId = parseInt(ev.dataTransfer.getData("taskId"));
const column = ev.target.closest('.kanban-column');
if (column) {
const newStatus = column.getAttribute('data-status');
const task = tasks.find(t => t.id === taskId);
if (task && task.status !== newStatus) {
task.status = newStatus;
renderBoard();
// Add to activity
const user = teamMembers[0]; // Current user
activities.unshift({
user: user.id,
action: 'moved',
target: task.title,
from: task.status,
to: newStatus,
time: 'Just now'
});
renderActivity();
}
}
document.querySelectorAll('.task-card').forEach(card => {
card.classList.remove('dragging');
});
}
// Modal Functions
function openTaskModal() {
document.getElementById('taskModal').classList.remove('hidden');
selectedAssignee = null;
renderAssignees();
}
function closeTaskModal() {
document.getElementById('taskModal').classList.add('hidden');
document.getElementById('newTaskForm').reset();
}
function handleNewTask(e) {
e.preventDefault();
const title = document.getElementById('taskTitle').value;
const type = document.getElementById('taskType').value;
const priority = document.getElementById('taskPriority').value;
if (!selectedAssignee) {
alert('Please select an assignee');
return;
}
const newTask = {
id: tasks.length + 1,
title: title,
type: type,
priority: priority,
status: 'todo',
assignee: selectedAssignee,
tags: [type.charAt(0).toUpperCase() + type.slice(1)],
comments: 0
};
tasks.push(newTask);
renderBoard();
closeTaskModal();
// Add activity
activities.unshift({
user: selectedAssignee,
action: 'created',
target: title,
time: 'Just now'
});
renderActivity();
}
// Close modal on outside click
document.getElementById('taskModal').addEventListener('click', (e) => {
if (e.target === e.currentTarget) {
closeTaskModal();
}
});