En esta página

Proyecto Final — Kanban Board

25 min lectura TextoCap. 5 — Producción

¡Felicitaciones por llegar al proyecto final!

Has completado todas las lecciones del curso y ahora es momento de aplicar todo lo aprendido en un proyecto real y complejo: un tablero Kanban completamente funcional.

¿Qué construirás?

El tablero Kanban incluye:

  • 4 columnas: Pendiente → En Progreso → En Revisión → Completada
  • Tarjetas de tarea con título, descripción, prioridad, asignado, etiquetas y fecha de vencimiento
  • Drag & Drop nativo con HTML5 para mover tarjetas entre columnas
  • Límites WIP (Work In Progress) por columna
  • Filtrado por texto y prioridad en tiempo real
  • Persistencia en localStorage con Zustand persist
  • Modal para crear nuevas tareas
  • Accesibilidad con ARIA roles y labels correctos

Arquitectura del proyecto

src/
├── components/
│   ├── TarjetaTarea.tsx    — Tarjeta draggable con prioridad y metadatos
│   ├── ColumnaKanban.tsx   — Drop zone con límite WIP
│   ├── TableroKanban.tsx   — Tablero raíz con estado de arrastre
│   ├── BotonAgregarTarea.tsx — Modal de creación
│   └── FiltroTablero.tsx   — Barra de filtros
├── store/
│   └── kanbanStore.ts      — Zustand store con devtools + persist
├── hooks/
│   └── useDragDrop.ts      — Hook personalizado para drag & drop
├── types/
│   └── kanban.ts           — Interfaces TypeScript
└── App.tsx                 — Router con rutas Inicio y Tablero

Tecnologías utilizadas

Tecnología Uso en el proyecto
React 19.2 Componentes y hooks
TypeScript Tipado estricto de todo el dominio
Zustand Estado global con persist + devtools
React Router v7 Navegación (Inicio / Tablero)
HTML5 Drag & Drop Mover tarjetas entre columnas
CSS personalizado Glassmorphism cards y grid layout

Cómo extender el proyecto

Una vez que el tablero básico funcione, practica estas extensiones:

Extensión 1 — TanStack Query para datos del servidor

// Obtener tareas desde una API real en lugar de localStorage
const { data: tareas } = useQuery({
  queryKey: ['tareas-kanban'],
  queryFn: () => fetch('/api/tareas').then(r => r.json()) as Promise<Tarea[]>,
});

const moverMutation = useMutation({
  mutationFn: ({ id, estado }: { id: string; estado: string }) =>
    fetch(`/api/tareas/${id}`, {
      method: 'PATCH',
      body: JSON.stringify({ estado }),
    }),
  onSuccess: () => void queryClient.invalidateQueries({ queryKey: ['tareas-kanban'] }),
});

Extensión 2 — Autenticación con Context

function KanbanConAuth(): React.JSX.Element {
  const { usuario } = useAuth();

  return usuario ? <TableroKanban /> : <PaginaLogin />;
}

Extensión 3 — React Router para detalle de tarea

// Ruta de detalle /tablero/tareas/:tareaId
function DetalleTarea(): React.JSX.Element {
  const { tareaId } = useParams<{ tareaId: string }>();
  const tarea = useKanban((s) => s.tareas.find((t) => t.id === tareaId));

  if (!tarea) return <Navigate to="/tablero" replace />;

  return (
    <article>
      <h1>{tarea.titulo}</h1>
      <p>{tarea.descripcion}</p>
      {/* Editor completo de la tarea */}
    </article>
  );
}

Extensión 4 — Testing del tablero

describe('TableroKanban', () => {
  it('mueve una tarea al columna En Progreso', async () => {
    const usuario = userEvent.setup();
    render(<TableroKanban />);

    // Verificar que la tarea existe en Pendiente
    const columnasPendiente = screen.getByRole('region', { name: /pendiente/i });
    expect(within(columnasPendiente).getAllByRole('article').length).toBeGreaterThan(0);
  });

  it('crea una nueva tarea correctamente', async () => {
    const usuario = userEvent.setup();
    render(<TableroKanban />);

    await usuario.click(screen.getByRole('button', { name: /nueva tarea/i }));

    const dialog = screen.getByRole('dialog');
    await usuario.type(within(dialog).getByLabelText(/título/i), 'Mi nueva tarea');
    await usuario.click(within(dialog).getByRole('button', { name: /agregar/i }));

    expect(screen.getByText('Mi nueva tarea')).toBeInTheDocument();
  });
});

Reflexión final

Este tablero Kanban integra todos los conceptos del curso:

  • Componentes y JSX — TarjetaTarea, ColumnaKanban, TableroKanban
  • Props y children — comunicación entre componentes del tablero
  • useState — estado local del modal, drag, expansión de tarjetas
  • useEffect — podría integrarse para sincronización con API
  • useRef — referencia al ID de tarea arrastrada (no causa render)
  • useCallback — callbacks estables para drag handlers
  • Hooks personalizados — useDragDrop, useKanbanFilters
  • Context API — podría usarse para auth
  • React Router — navegación entre tableros o detalle de tarea
  • Zustand — store global con persist y devtools
  • TypeScript — todos los tipos del dominio Kanban bien definidos

¡El tablero está listo para escalar! Puedes agregar colaboración en tiempo real con WebSockets, sub-tareas, adjuntos, comentarios, notificaciones y mucho más. El conocimiento que adquiriste en este curso te da todas las herramientas para construir aplicaciones React profesionales y escalables.

La API HTML Drag and Drop nativa funciona sin librerías
Este proyecto usa la API nativa de drag and drop (atributos draggable, onDragStart, onDragOver, onDrop). Para casos más avanzados (touch support, animaciones fluidas, accesibilidad completa), considera @dnd-kit/core que es la librería de drag & drop más moderna para React.
Agrega estilos CSS para completar el tablero
El código de este playground implementa toda la lógica. Para que se vea como un tablero real, agrega CSS con grid para las columnas, box-shadow para las tarjetas, transiciones en el drag y colores según la prioridad. El tablero debería verse funcional con CSS básico de Tailwind o módulos CSS.
crypto.randomUUID requiere HTTPS en producción
crypto.randomUUID() está disponible en contextos seguros (HTTPS) y localhost. En producción siempre estará en HTTPS. Si necesitas IDs en HTTP (poco común), usa nanoid como alternativa: npm install nanoid y import { nanoid } from 'nanoid'.