En esta página

Proyecto final - Gestor de tareas

25 min lectura TextoCap. 5 — JavaScript moderno

Descripcion del proyecto

En este proyecto final vas a construir un gestor de tareas que integra todos los conceptos del curso. El objetivo es que veas como encajan juntas todas las piezas que aprendiste.

Conceptos aplicados

Leccion Concepto usado en el proyecto
Variables y tipos Tipos primitivos, const/let
Operadores y control Condicionales, switch
Funciones modernas Arrow functions, closures, callbacks
Objetos y arrays Destructuring, spread
Métodos de array filter, map, reduce, find
Strings Template literals, includes, toLowerCase
DOM createElement, classList, event listeners
Eventos Event delegation, preventDefault
Promesas Manejo de operaciones asincronas
Async/Await Peticiones HTTP
Fetch API CRUD con una API REST
Modulos ES Organizacion en archivos
Patrones modernos Map, Set, campos privados, Optional chaining

Arquitectura

El proyecto sigue una arquitectura en capas:

1. Modelo (Task)

La clase Task encapsula los datos de una tarea con campos privados (#id, #completada). Usa el constructor para validar y inicializar, y expone getters para acceso de solo lectura.

2. Servicio (TaskStore)

El TaskStore gestiona la colección de tareas usando un Map para acceso eficiente por ID. Implementa un sistema de suscripciones (patrón Observer) para notificar cambios.

3. Vista (DOM)

La capa de vista (no incluida en el ejemplo, pero que tu construiras) usa los métodos del DOM para renderizar las tareas y event delegation para manejar las interacciones.

Desafios propuestos

Extiende el proyecto con estas mejoras progresivas:

Nivel 1 - Persistencia

Guarda las tareas en localStorage para que persistan entre recargas:

// Guardar
localStorage.setItem('tareas', JSON.stringify([...tareas]));

// Cargar
const guardadas = JSON.parse(localStorage.getItem('tareas') ?? '[]');

Nivel 2 - Interfaz DOM

Renderiza las tareas en el navegador:

  • Crea un formulario para agregar tareas
  • Muestra la lista con filtros (todas, pendientes, completadas)
  • Usa event delegation para manejar clicks en botones de eliminar y completar

Nivel 3 - API REST

Conecta el store a una API REST con fetch:

async function sincronizar() {
  const res = await fetch('/api/tareas');
  const datos = await res.json();
  // actualizar el store local
}

Nivel 4 - Busqueda avanzada

Implementa busqueda con debounce para filtrar tareas mientras el usuario escribe:

function debounce(fn, ms) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), ms);
  };
}

Recapitulacion del curso

Has completado JavaScript Esencial. Ahora dominas:

  • Fundamentos del lenguaje (variables, tipos, operadores, control de flujo)
  • Funciones modernas (arrow, closures, higher-order)
  • Estructuras de datos (objetos, arrays, Map, Set)
  • Manipulacion del DOM y manejo de eventos
  • Programación asincrona (promesas, async/await, fetch)
  • Modulos ES y patrones modernos

El siguiente paso es aprender un framework como Angular para construir aplicaciones web completas y escalables.


Has completado el curso de JavaScript Esencial. El siguiente paso recomendado es Angular para Desarrolladores.

Extiende el proyecto
Puedes extender este proyecto agregando persistencia con localStorage, renderizado en el DOM con createElement, o integración con una API REST usando fetch. Cada mejora refuerza los conceptos del curso.
Campos privados (#)
Los campos con
javascript
// =============================================
// PROYECTO: Gestor de tareas con JavaScript
// =============================================
// Aplica todo lo aprendido en el curso:
// - Modulos y clases
// - Métodos de array
// - Async/await y Fetch
// - DOM y eventos
// - Patrones modernos
// =============================================

// === MODELO: Clase Task ===
class Task {
  #id;
  #completada;

  constructor({ título, descripción = '', prioridad = 'media' }) {
    this.#id = crypto.randomUUID();
    this.título = título;
    this.descripción = descripción;
    this.prioridad = prioridad;
    this.#completada = false;
    this.creadaEn = new Date();
  }

  get id() { return this.#id; }
  get completada() { return this.#completada; }

  toggleCompletada() {
    this.#completada = !this.#completada;
  }

  toJSON() {
    return {
      id: this.#id,
      título: this.título,
      descripción: this.descripción,
      prioridad: this.prioridad,
      completada: this.#completada,
      creadaEn: this.creadaEn.toISOString(),
    };
  }
}

// === SERVICIO: TaskStore ===
class TaskStore {
  #tareas = new Map();
  #listeners = new Set();

  agregar(datos) {
    const tarea = new Task(datos);
    this.#tareas.set(tarea.id, tarea);
    this.#notificar();
    return tarea;
  }

  eliminar(id) {
    const eliminada = this.#tareas.delete(id);
    if (eliminada) this.#notificar();
    return eliminada;
  }

  toggleCompletada(id) {
    const tarea = this.#tareas.get(id);
    tarea?.toggleCompletada();
    this.#notificar();
  }

  obtenerTodas(filtro = 'todas') {
    const lista = [...this.#tareas.values()];
    switch (filtro) {
      case 'pendientes':
        return lista.filter(t => !t.completada);
      case 'completadas':
        return lista.filter(t => t.completada);
      default:
        return lista;
    }
  }

  buscar(término) {
    const lower = término.toLowerCase();
    return this.obtenerTodas().filter(t =>
      t.título.toLowerCase().includes(lower) ||
      t.descripción.toLowerCase().includes(lower)
    );
  }

  get estadisticas() {
    const todas = this.obtenerTodas();
    return {
      total: todas.length,
      completadas: todas.filter(t => t.completada).length,
      pendientes: todas.filter(t => !t.completada).length,
      porPrioridad: Object.groupBy(todas, t => t.prioridad),
    };
  }

  suscribir(callback) {
    this.#listeners.add(callback);
    return () => this.#listeners.delete(callback);
  }

  #notificar() {
    this.#listeners.forEach(fn => fn(this.obtenerTodas()));
  }
}

// === USO ===
const store = new TaskStore();

// Suscribirse a cambios
store.suscribir((tareas) => {
  console.log(`Tareas actualizadas: ${tareas.length}`);
});

// Agregar tareas
const t1 = store.agregar({
  título: 'Aprender JavaScript',
  descripción: 'Completar el curso de JS Esencial',
  prioridad: 'alta',
});

store.agregar({
  título: 'Practicar métodos de array',
  prioridad: 'media',
});

store.agregar({
  título: 'Leer documentación de MDN',
  prioridad: 'baja',
});

// Completar una tarea
store.toggleCompletada(t1.id);

// Ver estadisticas
console.log(store.estadisticas);

// Filtrar
console.log('Pendientes:', store.obtenerTodas('pendientes'));

// Buscar
console.log('Buscar:', store.buscar('array'));