| <!DOCTYPE html> |
| <html lang="es"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Tablero Kanban</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: 300px; |
| transition: all 0.3s ease; |
| } |
| .kanban-card { |
| transition: transform 0.2s ease, box-shadow 0.2s ease; |
| cursor: grab; |
| user-select: none; |
| } |
| .kanban-card:active { |
| cursor: grabbing; |
| } |
| .kanban-card.dragging { |
| opacity: 0.5; |
| transform: scale(1.02); |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.2); |
| } |
| .kanban-column.drop-zone { |
| background-color: rgba(209, 250, 229, 0.3); |
| border: 2px dashed #10b981; |
| } |
| .add-card-input { |
| transition: all 0.3s ease; |
| max-height: 0; |
| overflow: hidden; |
| } |
| .add-card-input.active { |
| max-height: 100px; |
| padding: 8px; |
| margin-top: 8px; |
| } |
| </style> |
| </head> |
| <body class="bg-gray-100 min-h-screen p-4"> |
| <div class="container mx-auto"> |
| <h1 class="text-3xl font-bold text-center mb-8 text-gray-800">Tablero Kanban</h1> |
| |
| <div class="flex flex-col md:flex-row gap-4"> |
| |
| <div class="kanban-column bg-white rounded-lg shadow-md flex-1" data-status="todo"> |
| <div class="p-4 border-b border-gray-200 flex justify-between items-center bg-blue-50 rounded-t-lg"> |
| <h2 class="font-semibold text-blue-700">Por hacer</h2> |
| <span class="bg-blue-100 text-blue-800 text-xs font-medium px-2.5 py-0.5 rounded-full"> |
| <span id="todo-count">3</span> tareas |
| </span> |
| </div> |
| <div class="p-2 space-y-2 todo-cards"> |
| |
| <div class="kanban-card p-3 bg-white rounded border border-gray-200 shadow-sm" draggable="true" data-task-id="1"> |
| <div class="flex justify-between items-start"> |
| <h3 class="font-medium text-gray-800">Diseñar interfaz de usuario</h3> |
| <button class="text-gray-400 hover:text-red-500 delete-task"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <p class="text-sm text-gray-500 mt-1">Crear wireframes para la nueva aplicación</p> |
| <div class="flex items-center mt-2 text-xs text-gray-400"> |
| <i class="far fa-calendar-alt mr-1"></i> |
| <span>Para hoy</span> |
| </div> |
| </div> |
| |
| <div class="kanban-card p-3 bg-white rounded border border-gray-200 shadow-sm" draggable="true" data-task-id="2"> |
| <div class="flex justify-between items-start"> |
| <h3 class="font-medium text-gray-800">Reunión con el equipo</h3> |
| <button class="text-gray-400 hover:text-red-500 delete-task"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <p class="text-sm text-gray-500 mt-1">Discutir los requerimientos del proyecto</p> |
| </div> |
| |
| <div class="kanban-card p-3 bg-white rounded border border-gray-200 shadow-sm" draggable="true" data-task-id="3"> |
| <div class="flex justify-between items-start"> |
| <h3 class="font-medium text-gray-800">Investigar tecnologías</h3> |
| <button class="text-gray-400 hover:text-red-500 delete-task"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <p class="text-sm text-gray-500 mt-1">Comparar frameworks para el frontend</p> |
| </div> |
| </div> |
| <div class="p-2"> |
| <button class="add-task-btn w-full py-2 text-sm text-blue-600 hover:text-blue-800 flex items-center justify-center"> |
| <i class="fas fa-plus mr-2"></i> Añadir tarea |
| </button> |
| <div class="add-card-input"> |
| <div class="flex"> |
| <input type="text" class="flex-1 border border-gray-300 rounded-l px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500" placeholder="Título de la tarea"> |
| <button class="bg-blue-500 text-white px-3 py-2 rounded-r text-sm hover:bg-blue-600 confirm-add"> |
| <i class="fas fa-check"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="kanban-column bg-white rounded-lg shadow-md flex-1" data-status="progress"> |
| <div class="p-4 border-b border-gray-200 flex justify-between items-center bg-yellow-50 rounded-t-lg"> |
| <h2 class="font-semibold text-yellow-700">En progreso</h2> |
| <span class="bg-yellow-100 text-yellow-800 text-xs font-medium px-2.5 py-0.5 rounded-full"> |
| <span id="progress-count">2</span> tareas |
| </span> |
| </div> |
| <div class="p-2 space-y-2 progress-cards"> |
| <div class="kanban-card p-3 bg-white rounded border border-gray-200 shadow-sm" draggable="true" data-task-id="4"> |
| <div class="flex justify-between items-start"> |
| <h3 class="font-medium text-gray-800">Desarrollar funcionalidad principal</h3> |
| <button class="text-gray-400 hover:text-red-500 delete-task"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <p class="text-sm text-gray-500 mt-1">Implementar el sistema de arrastrar y soltar</p> |
| <div class="flex items-center mt-2 text-xs text-gray-400"> |
| <i class="fas fa-user mr-1"></i> |
| <span>Asignado a: Juan</span> |
| </div> |
| </div> |
| |
| <div class="kanban-card p-3 bg-white rounded border border-gray-200 shadow-sm" draggable="true" data-task-id="5"> |
| <div class="flex justify-between items-start"> |
| <h3 class="font-medium text-gray-800">Escribir documentación</h3> |
| <button class="text-gray-400 hover:text-red-500 delete-task"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <p class="text-sm text-gray-500 mt-1">Documentar la API del backend</p> |
| </div> |
| </div> |
| <div class="p-2"> |
| <button class="add-task-btn w-full py-2 text-sm text-yellow-600 hover:text-yellow-800 flex items-center justify-center"> |
| <i class="fas fa-plus mr-2"></i> Añadir tarea |
| </button> |
| <div class="add-card-input"> |
| <div class="flex"> |
| <input type="text" class="flex-1 border border-gray-300 rounded-l px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-yellow-500" placeholder="Título de la tarea"> |
| <button class="bg-yellow-500 text-white px-3 py-2 rounded-r text-sm hover:bg-yellow-600 confirm-add"> |
| <i class="fas fa-check"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="kanban-column bg-white rounded-lg shadow-md flex-1" data-status="done"> |
| <div class="p-4 border-b border-gray-200 flex justify-between items-center bg-green-50 rounded-t-lg"> |
| <h2 class="font-semibold text-green-700">Hecho</h2> |
| <span class="bg-green-100 text-green-800 text-xs font-medium px-2.5 py-0.5 rounded-full"> |
| <span id="done-count">1</span> tarea |
| </span> |
| </div> |
| <div class="p-2 space-y-2 done-cards"> |
| <div class="kanban-card p-3 bg-white rounded border border-gray-200 shadow-sm" draggable="true" data-task-id="6"> |
| <div class="flex justify-between items-start"> |
| <h3 class="font-medium text-gray-800">Configurar entorno de desarrollo</h3> |
| <button class="text-gray-400 hover:text-red-500 delete-task"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <p class="text-sm text-gray-500 mt-1">Instalar todas las dependencias necesarias</p> |
| <div class="flex items-center mt-2 text-xs text-gray-400"> |
| <i class="far fa-check-circle mr-1 text-green-500"></i> |
| <span>Completado ayer</span> |
| </div> |
| </div> |
| </div> |
| <div class="p-2"> |
| <button class="add-task-btn w-full py-2 text-sm text-green-600 hover:text-green-800 flex items-center justify-center"> |
| <i class="fas fa-plus mr-2"></i> Añadir tarea |
| </button> |
| <div class="add-card-input"> |
| <div class="flex"> |
| <input type="text" class="flex-1 border border-gray-300 rounded-l px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-green-500" placeholder="Título de la tarea"> |
| <button class="bg-green-500 text-white px-3 py-2 rounded-r text-sm hover:bg-green-600 confirm-add"> |
| <i class="fas fa-check"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| |
| let draggedItem = null; |
| let currentColumn = null; |
| |
| |
| const todoCount = document.getElementById('todo-count'); |
| const progressCount = document.getElementById('progress-count'); |
| const doneCount = document.getElementById('done-count'); |
| |
| |
| function updateCounters() { |
| todoCount.textContent = document.querySelector('.todo-cards').children.length; |
| progressCount.textContent = document.querySelector('.progress-cards').children.length; |
| doneCount.textContent = document.querySelector('.done-cards').children.length; |
| } |
| |
| |
| const cards = document.querySelectorAll('.kanban-card'); |
| cards.forEach(card => { |
| card.addEventListener('dragstart', function() { |
| draggedItem = this; |
| currentColumn = this.parentElement; |
| setTimeout(() => { |
| this.classList.add('dragging'); |
| }, 0); |
| }); |
| |
| card.addEventListener('dragend', function() { |
| this.classList.remove('dragging'); |
| draggedItem = null; |
| currentColumn = null; |
| }); |
| |
| |
| const deleteBtn = card.querySelector('.delete-task'); |
| deleteBtn.addEventListener('click', function(e) { |
| e.stopPropagation(); |
| if (confirm('¿Estás seguro de eliminar esta tarea?')) { |
| card.remove(); |
| updateCounters(); |
| } |
| }); |
| }); |
| |
| |
| const columns = document.querySelectorAll('.kanban-column'); |
| columns.forEach(column => { |
| column.addEventListener('dragover', function(e) { |
| e.preventDefault(); |
| this.classList.add('drop-zone'); |
| }); |
| |
| column.addEventListener('dragleave', function() { |
| this.classList.remove('drop-zone'); |
| }); |
| |
| column.addEventListener('drop', function(e) { |
| e.preventDefault(); |
| this.classList.remove('drop-zone'); |
| |
| if (draggedItem && this !== currentColumn) { |
| const cardsContainer = this.querySelector('.kanban-column > div:nth-child(2)'); |
| cardsContainer.appendChild(draggedItem); |
| updateCounters(); |
| } |
| }); |
| }); |
| |
| |
| const addTaskBtns = document.querySelectorAll('.add-task-btn'); |
| addTaskBtns.forEach(btn => { |
| btn.addEventListener('click', function() { |
| const column = this.closest('.kanban-column'); |
| const inputContainer = column.querySelector('.add-card-input'); |
| inputContainer.classList.toggle('active'); |
| }); |
| }); |
| |
| const confirmAddBtns = document.querySelectorAll('.confirm-add'); |
| confirmAddBtns.forEach(btn => { |
| btn.addEventListener('click', function() { |
| const column = this.closest('.kanban-column'); |
| const input = column.querySelector('.add-card-input input'); |
| const title = input.value.trim(); |
| |
| if (title) { |
| const status = column.dataset.status; |
| const cardsContainer = column.querySelector(`.${status}-cards`); |
| |
| |
| const newCard = document.createElement('div'); |
| newCard.className = 'kanban-card p-3 bg-white rounded border border-gray-200 shadow-sm'; |
| newCard.setAttribute('draggable', 'true'); |
| newCard.setAttribute('data-task-id', Date.now()); |
| |
| newCard.innerHTML = ` |
| <div class="flex justify-between items-start"> |
| <h3 class="font-medium text-gray-800">${title}</h3> |
| <button class="text-gray-400 hover:text-red-500 delete-task"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <p class="text-sm text-gray-500 mt-1">Descripción de la tarea</p> |
| <div class="flex items-center mt-2 text-xs text-gray-400"> |
| <i class="far fa-calendar-alt mr-1"></i> |
| <span>Nueva tarea</span> |
| </div> |
| `; |
| |
| |
| newCard.addEventListener('dragstart', function() { |
| draggedItem = this; |
| currentColumn = this.parentElement; |
| setTimeout(() => { |
| this.classList.add('dragging'); |
| }, 0); |
| }); |
| |
| newCard.addEventListener('dragend', function() { |
| this.classList.remove('dragging'); |
| draggedItem = null; |
| currentColumn = null; |
| }); |
| |
| |
| const deleteBtn = newCard.querySelector('.delete-task'); |
| deleteBtn.addEventListener('click', function(e) { |
| e.stopPropagation(); |
| if (confirm('¿Estás seguro de eliminar esta tarea?')) { |
| newCard.remove(); |
| updateCounters(); |
| } |
| }); |
| |
| cardsContainer.appendChild(newCard); |
| input.value = ''; |
| column.querySelector('.add-card-input').classList.remove('active'); |
| updateCounters(); |
| } |
| }); |
| }); |
| |
| |
| const inputs = document.querySelectorAll('.add-card-input input'); |
| inputs.forEach(input => { |
| input.addEventListener('keypress', function(e) { |
| if (e.key === 'Enter') { |
| this.closest('.kanban-column').querySelector('.confirm-add').click(); |
| } |
| }); |
| }); |
| }); |
| </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=Backtun/tablero-kanban-deepsite" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body> |
| </html> |