Ir al contenido

Tablero Kanban

El tablero Kanban es el corazón del producto: drag & drop, multi-board, CRUD de columnas y tareas, toolbar de filtros y ordenación, historial de actividad y descripciones en markdown.

/kanban → primer board del workspace (getBoardAction)
/kanban/[boardId] → board concreto (getBoardByIdAction)

El CRUD de boards (listar, crear, renombrar, eliminar) vive en la sección “Boards” de la sidebar:

  • Crear: botón +createBoardAction
  • Renombrar: lápiz → input inline → renameBoardAction
  • Eliminar: papelera → confirmación → deleteBoardAction
  • Cada board enlaza a /kanban/[boardId] y se marca como activo con borde primary.

El tablero usa @dnd-kit para el drag & drop con tres niveles:

DndContext (KanbanBoard)
├── Droppable (KanbanColumn × N)
│ └── SortableContext
│ └── useSortable (TaskCard × N)
└── DragOverlay (copia visual mientras se arrastra)
  • react-beautiful-dnd está deprecado (no soporta React 18+)
  • @dnd-kit es la alternativa moderna con API similar
  • Soporta React 19, es modular (core + sortable + utilities ~20KB), y permite DnD entre contenedores

components/kanban/KanbanBoard.tsx

Componente principal (client component). Obtiene el board (getBoardAction o getBoardByIdAction), los miembros con getWorkspaceMembersAction, y orquesta todo el estado.

Prop Tipo Descripción
params Promise<{ workspaceId: string; boardId?: string }> Parámetros de ruta

Estado local:

  • board — estado con optimistic updates (se actualiza en cliente antes de la server action)
  • activeTask — tarea arrastrada (para DragOverlay)
  • members — miembros del workspace para asignaciones
  • searchQuery / priorityFilter / assigneeFilter — filtros de la toolbar
  • columnWidth / cardDensity / cardFontSize / showDescriptions / fillWidth — preferencias de vista
  • columnSortState — ordenación por columna (default | priority | dueDate | title | createdAt, asc/desc)
  • editingTask — tarea en edición (abre TaskFormDialog)

Preferencias de vista se persisten en localStorage bajo la clave syncro.kanban.view.v1.

Callbacks que pasa a las columnas:

Callback Server Action
handleColumnNameChange updateColumnNameAction
handleColorChange updateColumnColorAction
handleDeleteColumn deleteColumnAction
handleDeleteTask deleteTaskAction
handlePriorityChange updateTaskAction
handleAssigneeChange toggleAssigneeAction
handleMoveToBacklog moveTaskToBacklogAction
handleAddColumn createColumnAction
handleEditTask abre TaskFormDialog (edit)

components/kanban/KanbanToolbar.tsx

Barra de controles por encima del tablero:

Control Comportamiento
Búsqueda Filtra por título y descripción
Filtro prioridad Multiselect LOW/MEDIUM/HIGH/URGENT con contador
Filtro asignado Multiselect por miembro del workspace
Ancho de columna compact / normal / wide / extraWide
Densidad de tarjeta compact / normal / roomy
Tamaño de letra compact / normal / large / extraLarge
Descripciones Muestra/oculta descripciones en las tarjetas
Fill width Columnas ocupan todo el ancho disponible
Limpiar filtros Solo aparece si hay filtros activos

Los menús desplegables usan createPortal y se posicionan con position: fixed bajo el trigger.

components/kanban/KanbanColumn.tsx

Columna dropeable con menú contextual, ordenación propia y estados de filtrado.

Prop Tipo Descripción
id string ID de la columna
name string Nombre visible
color string | null Color de fondo del header
tasks TaskData[] Tareas (ya filtradas/ordenadas)
workspaceId string Para server actions
members Member[] Miembros del workspace
searchQuery / priorityFilter / assigneeFilter Estado de filtrado (para vaciado de columna)
sortBy / sortDir Ordenación actual de la columna
onSortChange (columnId, field, dir) => void Cambiar ordenación
widthClass / densityClass / cardDensity / fontSize Preferencias de vista
onMoveToBacklog (taskId) => void Mover tarea al backlog

Características:

  • Header con color dinámico, nombre editable inline, contador de tareas y menú de ordenación
  • Menú (MoreHorizontal) con selector de 7 colores, ordenación y opción de eliminar
  • Confirm dialog al eliminar si tiene tareas
  • Botones “Añadir tarea” (TaskFormDialog) y “Añadir desde backlog” (AddBacklogTaskDialog)
  • useDroppable de @dnd-kit con feedback visual al hover
  • SortableContext con estrategia vertical

components/kanban/TaskCard.tsx

Card de tarea arrastrable (sortable).

Prop Tipo Descripción
id string ID de la tarea
title string Título
description string | null Descripción (markdown, expandible)
priority "LOW" | "MEDIUM" | "HIGH" | "URGENT" Prioridad
creatorName string | null Creador de la tarea
createdAt Date | string | null Fecha de creación
assigneeNames / assigneeIds string[] Asignados
members / workspaceId Para dropdown de asignación
isOverlay boolean Modo copia (DragOverlay)
showDescriptions / density / fontSize Preferencias de vista
onEdit (taskId) => void Abrir edición
onMoveToBacklog (taskId) => void Mover a backlog

Características:

  • useSortable de @dnd-kit con feedback visual durante drag
  • Dropdown inline de prioridad LOW→MEDIUM→HIGH→URGENT
  • Dropdown de asignación con avatares (toggle on/off) y AvatarStack
  • Descripción en markdown expandible (icono chevron)
  • Creador + fecha de creación, confirm inline al eliminar, editar y mover a backlog

components/tasks/TaskFormDialog.tsx

Modal de crear y editar tareas (sustituye al antiguo CreateTaskDialog).

Prop Tipo Descripción
mode "create" | "edit" Modo del modal
workspaceId string Para server action
columnId string (solo create) Columna destino
task Task (solo edit) Tarea a editar
members Member[] Miembros para asignación
onClose / onSaved () => void Cerrar / refrescar

Campos:

  • Título (requerido, max 200)
  • Descripción con MarkdownEditor (vista previa + modal de ayuda)
  • Prioridad (Baja/Media/Alta/Urgente)
  • Asignado (dropdown con avatares)
  • Fecha inicio / fecha límite (dueDate, startDate)
  • Horas estimadas (estimatedHours)

components/kanban/TaskActivityDialog.tsx

Modal con el historial de actividad de una tarea. Cada entrada muestra el usuario, el tipo de acción (TaskAction con icono y color) y, cuando aplica, el cambio de campo (fieldName/oldValue/newValue). Carga con getTaskActivityAction.

components/kanban/AddBacklogTaskDialog.tsx

Modal para añadir una tarea del backlog a una columna. Lista las tareas sin columna (getUnallocatedTasksAction), permite buscar, y al confirmar mueve la tarea con moveTaskAction.

components/kanban/ConfirmDialog.tsx

Modal de confirmación reutilizable.

Prop Tipo Default Descripción
title string Título del modal
message string Mensaje descriptivo
confirmLabel string "Eliminar" Texto del botón confirmar
cancelLabel string "Cancelar" Texto del botón cancelar
destructive boolean true Estilo rojo de peligro
onConfirm () => void Acción al confirmar
onCancel () => void Acción al cancelar

components/kanban/KanbanSkeleton.tsx

Skeleton de carga del tablero mientras se obtienen los datos.

  1. DragStart: se identifica la tarea arrastrada y se muestra en DragOverlay
  2. DragMove: auto-scroll horizontal si el puntero se acerca a los bordes del contenedor
  3. DragOver: optimistic update (con debounce de 50ms para evitar el bucle infinito cuando el puntero alterna entre columnas A/B cada frame)
  4. DragEnd: se calcula la posición final, se aplica el optimistic update y se llama a moveTaskAction con la nueva columna y posición
  5. Si no hay cambio real (misma columna y posición), no se llama a la server action

Fixes aplicados (v0.5.0):

  • bugfix/kanban-drag-infinite-loop (#117): el drag-over se debouncea y solo muta estado cuando cambia de columna
  • fix/kanban-drag-drop-gap (#113): el hueco de arrastre sigue al puntero al mover tarjetas entre columnas
  • Estrategia de ordenación columnAwareVerticalSortingStrategy (lib/kanban/columnSortingStrategy.ts)

Las posiciones se normalizan (0, 1, 2, ...) tanto en servidor como en cliente.

Los filtros (búsqueda, prioridad, asignado) se aplican antes de ordenar en getSortedTasks. El orden por defecto es la position; alternativas: prioridad (LOW→URGENT), dueDate, title o createdAt, con dirección asc/desc por columna. Cuando una columna queda vacía por filtros, se muestra un mensaje con “Limpiar filtros”.

Las descripciones se escriben y renderizan en markdown:

  • MarkdownEditor — editor con barra de herramientas y vista previa
  • MarkdownView — render con react-markdown + remark-gfm + rehype-raw
  • MarkdownHelpModal — ayuda de sintaxis

Fix #118: las tablas markdown ya no desbordan la tarjeta (el contenedor controla el overflow).

Regla Lugar
Mínimo 3 columnas por board Server action deleteColumnAction
Máximo 7 columnas por board UI deshabilita botón + server validateCanAddColumn
Columna no vacía → confirm dialog antes de eliminar UI
Título de tarea requerido (max 200) Zod en createTaskAction
estimatedHours positivo Zod en createTaskAction / updateTaskAction
Solo miembros del workspace pueden crear/mover tareas Server action verifica membresía

Todas las claves bajo el namespace kanban en messages/{es,en}.json (más backlog para el diálogo de tareas y markdown para el editor):

Clave Propósito
kanban.title Título del tablero
kanban.addColumn Botón añadir columna
kanban.newColumnName Nombre por defecto de nueva columna
kanban.columnColor Selector de color
kanban.editColumnName Tooltip editar nombre
kanban.deleteColumn Eliminar columna
kanban.confirmDeleteColumnTitle/Message Confirmación con conteo de tareas
kanban.addTask / addTaskFromBacklog Añadir tarea / desde backlog
kanban.noTasks Estado vacío de columna
kanban.noBoardTitle/Desc + createBoard Estado sin tablero
kanban.searchTasks / filterPriority / filterAssignee / clearFilters Toolbar
kanban.columnWidth / width* Ancho de columna
kanban.cardDensity / density* Densidad
kanban.fontSize / font* Tamaño de letra
kanban.showDescriptions / fillWidth Controles de vista
kanban.priority* / assignee* / unassigned / noMembers Prioridades y asignaciones
kanban.boards / newBoard / renameBoard / deleteBoard Sección de boards en sidebar
kanban.create / creating / cancel / loading Diálogos