On this page

Final project - Task manager

25 min read TextCh. 5 — Modern JavaScript

Project description

In this final project you will build a task manager that integrates all the concepts from the course. The goal is for you to see how all the pieces you learned fit together.

Concepts applied

Lesson Concept used in the project
Variables and types Primitive types, const/let
Operators and control flow Conditionals, switch
Modern functions Arrow functions, closures, callbacks
Objects and arrays Destructuring, spread
Array methods filter, map, reduce, find
Strings Template literals, includes, toLowerCase
DOM createElement, classList, event listeners
Events Event delegation, preventDefault
Promises Handling asynchronous operations
Async/Await HTTP requests
Fetch API CRUD with a REST API
ES Modules File organization
Modern patterns Map, Set, private fields, Optional chaining

Architecture

The project follows a layered architecture:

1. Model (Task)

The Task class encapsulates the data of a task with private fields (#id, #completed). It uses the constructor to validate and initialize, and exposes getters for read-only access.

2. Service (TaskStore)

The TaskStore manages the task collection using a Map for efficient access by ID. It implements a subscription system (Observer pattern) to notify changes.

3. View (DOM)

The view layer (not included in the example, but which you will build) uses DOM methods to render tasks and event delegation to handle interactions.

Proposed challenges

Extend the project with these progressive improvements:

Level 1 - Persistence

Save tasks to localStorage so they persist between reloads:

// Save
localStorage.setItem('tasks', JSON.stringify([...tasks]));

// Load
const saved = JSON.parse(localStorage.getItem('tasks') ?? '[]');

Level 2 - DOM interface

Render tasks in the browser:

  • Create a form to add tasks
  • Display the list with filters (all, pending, completed)
  • Use event delegation to handle clicks on delete and complete buttons

Level 3 - REST API

Connect the store to a REST API with fetch:

async function sync() {
  const res = await fetch('/api/tasks');
  const data = await res.json();
  // update the local store
}

Implement search with debounce to filter tasks as the user types:

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

Course recap

You have completed Essential JavaScript. You now master:

  • Language fundamentals (variables, types, operators, control flow)
  • Modern functions (arrow, closures, higher-order)
  • Data structures (objects, arrays, Map, Set)
  • DOM manipulation and event handling
  • Asynchronous programming (promises, async/await, fetch)
  • ES modules and modern patterns

The next step is to learn a framework like Angular to build complete and scalable web applications.


You have completed the Essential JavaScript course. The recommended next step is Angular for Developers.

Extend the project
You can extend this project by adding persistence with localStorage, DOM rendering with createElement, or integration with a REST API using fetch. Each improvement reinforces the course concepts.
Private fields (#)
Fields with
javascript
// =============================================
// PROJECT: Task manager with JavaScript
// =============================================
// Applies everything learned in the course:
// - Modules and classes
// - Array methods
// - Async/await and Fetch
// - DOM and events
// - Modern patterns
// =============================================

// === MODEL: Task class ===
class Task {
  #id;
  #completed;

  constructor({ title, description = '', priority = 'medium' }) {
    this.#id = crypto.randomUUID();
    this.title = title;
    this.description = description;
    this.priority = priority;
    this.#completed = false;
    this.createdAt = new Date();
  }

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

  toggleCompleted() {
    this.#completed = !this.#completed;
  }

  toJSON() {
    return {
      id: this.#id,
      title: this.title,
      description: this.description,
      priority: this.priority,
      completed: this.#completed,
      createdAt: this.createdAt.toISOString(),
    };
  }
}

// === SERVICE: TaskStore ===
class TaskStore {
  #tasks = new Map();
  #listeners = new Set();

  add(data) {
    const task = new Task(data);
    this.#tasks.set(task.id, task);
    this.#notify();
    return task;
  }

  remove(id) {
    const removed = this.#tasks.delete(id);
    if (removed) this.#notify();
    return removed;
  }

  toggleCompleted(id) {
    const task = this.#tasks.get(id);
    task?.toggleCompleted();
    this.#notify();
  }

  getAll(filter = 'all') {
    const list = [...this.#tasks.values()];
    switch (filter) {
      case 'pending':
        return list.filter(t => !t.completed);
      case 'completed':
        return list.filter(t => t.completed);
      default:
        return list;
    }
  }

  search(term) {
    const lower = term.toLowerCase();
    return this.getAll().filter(t =>
      t.title.toLowerCase().includes(lower) ||
      t.description.toLowerCase().includes(lower)
    );
  }

  get stats() {
    const all = this.getAll();
    return {
      total: all.length,
      completed: all.filter(t => t.completed).length,
      pending: all.filter(t => !t.completed).length,
      byPriority: Object.groupBy(all, t => t.priority),
    };
  }

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

  #notify() {
    this.#listeners.forEach(fn => fn(this.getAll()));
  }
}

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

// Subscribe to changes
store.subscribe((tasks) => {
  console.log(`Tasks updated: ${tasks.length}`);
});

// Add tasks
const t1 = store.add({
  title: 'Learn JavaScript',
  description: 'Complete the Essential JS course',
  priority: 'high',
});

store.add({
  title: 'Practice array methods',
  priority: 'medium',
});

store.add({
  title: 'Read MDN documentation',
  priority: 'low',
});

// Complete a task
store.toggleCompleted(t1.id);

// View stats
console.log(store.stats);

// Filter
console.log('Pending:', store.getAll('pending'));

// Search
console.log('Search:', store.search('array'));