task-manager-2 / index.html
hharris928's picture
Add 1 files
3fb9c6b verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kanban Task Board</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.kanban-column {
min-height: 500px;
}
.task-card {
transition: all 0.2s ease;
}
.task-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.task-card.dragging {
opacity: 0.5;
transform: scale(1.02);
}
.modal {
transition: opacity 0.2s ease, transform 0.2s ease;
}
.modal.hidden {
opacity: 0;
pointer-events: none;
transform: translateY(20px);
}
</style>
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<div class="w-64 bg-indigo-800 text-white flex flex-col">
<div class="p-4 flex items-center justify-center border-b border-indigo-700">
<h1 class="text-2xl font-bold">TaskFlow</h1>
</div>
<nav class="flex-1 p-4">
<ul class="space-y-2">
<li>
<a href="#" class="flex items-center p-2 rounded-lg bg-indigo-700">
<i class="fas fa-tachometer-alt mr-3"></i>
<span>Overview</span>
</a>
</li>
<li>
<a href="#" class="flex items-center p-2 rounded-lg hover:bg-indigo-700">
<i class="fas fa-tasks mr-3"></i>
<span>Tasks</span>
</a>
</li>
<li>
<a href="#" class="flex items-center p-2 rounded-lg hover:bg-indigo-700">
<i class="fas fa-users mr-3"></i>
<span>Your Team</span>
</a>
</li>
<li>
<a href="#" class="flex items-center p-2 rounded-lg hover:bg-indigo-700">
<i class="fas fa-envelope mr-3"></i>
<span>Messages</span>
</a>
</li>
<li>
<a href="#" class="flex items-center p-2 rounded-lg hover:bg-indigo-700">
<i class="fas fa-cog mr-3"></i>
<span>Settings</span>
</a>
</li>
</ul>
</nav>
<div class="p-4 border-t border-indigo-700">
<div class="flex items-center">
<img src="https://randomuser.me/api/portraits/women/44.jpg" alt="User" class="w-10 h-10 rounded-full mr-3">
<div>
<p class="font-medium">Jane Doe</p>
<p class="text-xs text-indigo-300">Admin</p>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Header -->
<header class="bg-white border-b border-gray-200 p-4 flex items-center justify-between">
<div>
<h2 class="text-xl font-semibold">Task Board</h2>
<p class="text-sm text-gray-500">Manage your tasks efficiently</p>
</div>
<div class="flex items-center space-x-4">
<div class="relative">
<input type="text" placeholder="Search tasks..." class="pl-10 pr-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
</div>
<div class="flex items-center space-x-2">
<button id="theme-toggle" class="p-2 rounded-full hover:bg-gray-100">
<i class="fas fa-moon"></i>
</button>
<button class="p-2 rounded-full hover:bg-gray-100">
<i class="fas fa-bell"></i>
</button>
</div>
</div>
</header>
<!-- Content -->
<main class="flex-1 overflow-auto p-6">
<div class="flex justify-between items-center mb-6">
<div class="flex space-x-2">
<button class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700">
<i class="fas fa-plus mr-2"></i> Create Task
</button>
<button class="px-4 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-50">
<i class="fas fa-filter mr-2"></i> Filter
</button>
</div>
<div class="flex items-center space-x-2">
<span class="text-sm text-gray-500">View:</span>
<button class="px-3 py-1 bg-indigo-100 text-indigo-700 rounded-lg">Kanban</button>
<button class="px-3 py-1 bg-white border border-gray-300 rounded-lg hover:bg-gray-50">List</button>
</div>
</div>
<!-- Kanban Board -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<!-- To Do Column -->
<div class="kanban-column bg-gray-50 rounded-lg p-4">
<div class="flex justify-between items-center mb-4">
<h3 class="font-semibold text-gray-700">To Do</h3>
<span class="bg-gray-200 text-gray-700 text-xs px-2 py-1 rounded-full">5</span>
</div>
<div class="space-y-4" id="todo-column">
<!-- Task cards will be added here by JavaScript -->
</div>
</div>
<!-- In Progress Column -->
<div class="kanban-column bg-gray-50 rounded-lg p-4">
<div class="flex justify-between items-center mb-4">
<h3 class="font-semibold text-gray-700">In Progress</h3>
<span class="bg-blue-200 text-blue-700 text-xs px-2 py-1 rounded-full">3</span>
</div>
<div class="space-y-4" id="inprogress-column">
<!-- Task cards will be added here by JavaScript -->
</div>
</div>
<!-- Review Column -->
<div class="kanban-column bg-gray-50 rounded-lg p-4">
<div class="flex justify-between items-center mb-4">
<h3 class="font-semibold text-gray-700">Review</h3>
<span class="bg-yellow-200 text-yellow-700 text-xs px-2 py-1 rounded-full">2</span>
</div>
<div class="space-y-4" id="review-column">
<!-- Task cards will be added here by JavaScript -->
</div>
</div>
<!-- Done Column -->
<div class="kanban-column bg-gray-50 rounded-lg p-4">
<div class="flex justify-between items-center mb-4">
<h3 class="font-semibold text-gray-700">Done</h3>
<span class="bg-green-200 text-green-700 text-xs px-2 py-1 rounded-full">4</span>
</div>
<div class="space-y-4" id="done-column">
<!-- Task cards will be added here by JavaScript -->
</div>
</div>
</div>
</main>
</div>
</div>
<!-- Task Modal -->
<div id="task-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50 modal hidden">
<div class="bg-white rounded-lg w-full max-w-md">
<div class="p-4 border-b flex justify-between items-center">
<h3 class="text-lg font-semibold" id="modal-title">Create New Task</h3>
<button id="close-modal" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="p-4">
<form id="task-form">
<input type="hidden" id="task-id">
<div class="mb-4">
<label for="task-title" class="block text-sm font-medium text-gray-700 mb-1">Task Title</label>
<input type="text" id="task-title" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500" required>
</div>
<div class="mb-4">
<label for="task-description" class="block text-sm font-medium text-gray-700 mb-1">Description</label>
<textarea id="task-description" rows="3" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"></textarea>
</div>
<div class="mb-4">
<label for="task-status" class="block text-sm font-medium text-gray-700 mb-1">Status</label>
<select id="task-status" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<option value="todo">To Do</option>
<option value="inprogress">In Progress</option>
<option value="review">Review</option>
<option value="done">Done</option>
</select>
</div>
<div class="mb-4">
<label for="task-due" class="block text-sm font-medium text-gray-700 mb-1">Due Date</label>
<input type="date" id="task-due" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
</div>
<div class="flex justify-end space-x-2">
<button type="button" id="cancel-task" class="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50">Cancel</button>
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700">Save Task</button>
</div>
</form>
</div>
</div>
</div>
<script>
// Sample tasks data
let tasks = [
{ id: 1, title: 'Design new homepage', description: 'Create wireframes and mockups for the new homepage design', status: 'todo', due: '2023-06-15', created: '2023-05-20' },
{ id: 2, title: 'Implement user authentication', description: 'Set up login and registration functionality', status: 'inprogress', due: '2023-06-10', created: '2023-05-15' },
{ id: 3, title: 'Write API documentation', description: 'Document all endpoints for the REST API', status: 'review', due: '2023-06-05', created: '2023-05-10' },
{ id: 4, title: 'Fix mobile menu bug', description: 'Menu doesn\'t close properly on mobile devices', status: 'done', due: '2023-05-30', created: '2023-05-01' },
{ id: 5, title: 'Optimize database queries', description: 'Review and optimize slow database queries', status: 'todo', due: '2023-06-20', created: '2023-05-25' },
{ id: 6, title: 'Update dependencies', description: 'Update all project dependencies to latest versions', status: 'inprogress', due: '2023-06-12', created: '2023-05-18' },
{ id: 7, title: 'Create admin dashboard', description: 'Build interface for admin to manage users and content', status: 'review', due: '2023-06-08', created: '2023-05-12' },
{ id: 8, title: 'Setup CI/CD pipeline', description: 'Configure continuous integration and deployment', status: 'done', due: '2023-05-28', created: '2023-04-30' },
{ id: 9, title: 'Write unit tests', description: 'Add unit tests for core functionality', status: 'done', due: '2023-05-25', created: '2023-04-20' },
{ id: 10, title: 'Prepare presentation', description: 'Create slides for the upcoming client meeting', status: 'todo', due: '2023-06-18', created: '2023-05-22' }
];
// DOM elements
const todoColumn = document.getElementById('todo-column');
const inprogressColumn = document.getElementById('inprogress-column');
const reviewColumn = document.getElementById('review-column');
const doneColumn = document.getElementById('done-column');
const taskModal = document.getElementById('task-modal');
const taskForm = document.getElementById('task-form');
const closeModal = document.getElementById('close-modal');
const cancelTask = document.getElementById('cancel-task');
const createTaskBtn = document.querySelector('main button:first-child');
const themeToggle = document.getElementById('theme-toggle');
const modalTitle = document.getElementById('modal-title');
const taskIdInput = document.getElementById('task-id');
const taskTitleInput = document.getElementById('task-title');
const taskDescInput = document.getElementById('task-description');
const taskStatusInput = document.getElementById('task-status');
const taskDueInput = document.getElementById('task-due');
// Initialize the board
function renderTasks() {
// Clear all columns
todoColumn.innerHTML = '';
inprogressColumn.innerHTML = '';
reviewColumn.innerHTML = '';
doneColumn.innerHTML = '';
// Render tasks in their respective columns
tasks.forEach(task => {
const taskCard = createTaskCard(task);
switch(task.status) {
case 'todo':
todoColumn.appendChild(taskCard);
break;
case 'inprogress':
inprogressColumn.appendChild(taskCard);
break;
case 'review':
reviewColumn.appendChild(taskCard);
break;
case 'done':
doneColumn.appendChild(taskCard);
break;
}
});
// Update task counts
updateTaskCounts();
}
// Create a task card element
function createTaskCard(task) {
const card = document.createElement('div');
card.className = 'task-card bg-white p-4 rounded-lg shadow cursor-move border-l-4 ';
card.draggable = true;
card.dataset.id = task.id;
// Set border color based on status
switch(task.status) {
case 'todo':
card.classList.add('border-gray-400');
break;
case 'inprogress':
card.classList.add('border-blue-500');
break;
case 'review':
card.classList.add('border-yellow-500');
break;
case 'done':
card.classList.add('border-green-500');
break;
}
// Format due date
const dueDate = new Date(task.due);
const today = new Date();
today.setHours(0, 0, 0, 0);
const isOverdue = dueDate < today && task.status !== 'done';
const dueDateStr = dueDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
card.innerHTML = `
<div class="flex justify-between items-start mb-2">
<h4 class="font-medium">${task.title}</h4>
<div class="flex space-x-2">
<button class="edit-task text-gray-400 hover:text-indigo-600" data-id="${task.id}">
<i class="fas fa-edit"></i>
</button>
<button class="delete-task text-gray-400 hover:text-red-600" data-id="${task.id}">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<p class="text-sm text-gray-500 mb-3">${task.description}</p>
<div class="flex justify-between items-center text-xs">
<span class="text-gray-500">Due: <span class="${isOverdue ? 'text-red-500 font-medium' : 'text-gray-700'}">${dueDateStr}</span></span>
<span class="text-gray-500">Created: ${new Date(task.created).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}</span>
</div>
`;
// Add drag events
card.addEventListener('dragstart', dragStart);
card.addEventListener('dragend', dragEnd);
// Add edit and delete event listeners
card.querySelector('.edit-task').addEventListener('click', (e) => {
e.stopPropagation();
editTask(task.id);
});
card.querySelector('.delete-task').addEventListener('click', (e) => {
e.stopPropagation();
deleteTask(task.id);
});
return card;
}
// Update task counts in each column
function updateTaskCounts() {
const todoCount = document.querySelector('#todo-column + div span');
const inprogressCount = document.querySelector('#inprogress-column + div span');
const reviewCount = document.querySelector('#review-column + div span');
const doneCount = document.querySelector('#done-column + div span');
todoCount.textContent = tasks.filter(t => t.status === 'todo').length;
inprogressCount.textContent = tasks.filter(t => t.status === 'inprogress').length;
reviewCount.textContent = tasks.filter(t => t.status === 'review').length;
doneCount.textContent = tasks.filter(t => t.status === 'done').length;
}
// Drag and drop functionality
let draggedItem = null;
function dragStart(e) {
draggedItem = this;
this.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', this.innerHTML);
}
function dragEnd() {
this.classList.remove('dragging');
}
function dragOver(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
}
function dragEnter(e) {
e.preventDefault();
this.classList.add('bg-gray-100');
}
function dragLeave() {
this.classList.remove('bg-gray-100');
}
function drop(e) {
e.preventDefault();
this.classList.remove('bg-gray-100');
if (draggedItem !== this) {
const taskId = parseInt(draggedItem.dataset.id);
const newStatus = this.parentElement.id.split('-')[0];
// Update task status
const taskIndex = tasks.findIndex(t => t.id === taskId);
if (taskIndex !== -1) {
tasks[taskIndex].status = newStatus;
renderTasks();
}
}
}
// Add drag and drop events to columns
[todoColumn, inprogressColumn, reviewColumn, doneColumn].forEach(column => {
column.addEventListener('dragover', dragOver);
column.addEventListener('dragenter', dragEnter);
column.addEventListener('dragleave', dragLeave);
column.addEventListener('drop', drop);
});
// Task CRUD operations
function openModal() {
taskModal.classList.remove('hidden');
}
function closeModalHandler() {
taskModal.classList.add('hidden');
taskForm.reset();
taskIdInput.value = '';
}
function createTask() {
modalTitle.textContent = 'Create New Task';
openModal();
}
function editTask(id) {
const task = tasks.find(t => t.id === id);
if (task) {
modalTitle.textContent = 'Edit Task';
taskIdInput.value = task.id;
taskTitleInput.value = task.title;
taskDescInput.value = task.description;
taskStatusInput.value = task.status;
taskDueInput.value = task.due;
openModal();
}
}
function deleteTask(id) {
if (confirm('Are you sure you want to delete this task?')) {
tasks = tasks.filter(t => t.id !== id);
renderTasks();
}
}
function saveTask(e) {
e.preventDefault();
const taskData = {
title: taskTitleInput.value,
description: taskDescInput.value,
status: taskStatusInput.value,
due: taskDueInput.value,
created: new Date().toISOString().split('T')[0]
};
if (taskIdInput.value) {
// Update existing task
const taskIndex = tasks.findIndex(t => t.id === parseInt(taskIdInput.value));
if (taskIndex !== -1) {
tasks[taskIndex] = { ...tasks[taskIndex], ...taskData };
}
} else {
// Create new task
const newId = tasks.length > 0 ? Math.max(...tasks.map(t => t.id)) + 1 : 1;
tasks.push({ id: newId, ...taskData });
}
closeModalHandler();
renderTasks();
}
// Theme toggle
function toggleTheme() {
document.documentElement.classList.toggle('dark');
const icon = themeToggle.querySelector('i');
if (document.documentElement.classList.contains('dark')) {
icon.classList.replace('fa-moon', 'fa-sun');
document.body.classList.add('bg-gray-900');
document.body.classList.remove('bg-gray-100');
} else {
icon.classList.replace('fa-sun', 'fa-moon');
document.body.classList.remove('bg-gray-900');
document.body.classList.add('bg-gray-100');
}
}
// Event listeners
createTaskBtn.addEventListener('click', createTask);
closeModal.addEventListener('click', closeModalHandler);
cancelTask.addEventListener('click', closeModalHandler);
taskForm.addEventListener('submit', saveTask);
themeToggle.addEventListener('click', toggleTheme);
// Initialize
renderTasks();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=hharris928/task-manager-2" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>