¡Te damos la bienvenida a este reto!

1

¡Bienvenido al mundo de JavaScript!

Día 1

2

Variables, funciones y sintaxis básica

3

Tipos de datos

4

Playground - Retorna el tipo

5

Tipos de datos - pt 2

Día 2

6

Operadores

7

Hoisting y coerción

8

Playground - Calcula la propina

9

Alcance de las variables

Día 3

10

Condicionales

11

Playground - Calcula años bisiestos

12

Switch

13

Playground - Obten información de mascotas según su tipo

14

Ciclos

15

Playground - Dibuja un triangulo

Día 4

16

Arrays

17

Playground - Encuentra al michi mas famoso

18

Objetos

19

Playground - Obten el promedio de los estudiantes

Día 5 - Checkpoint

20

Playground - encuentra el palindromo más grande

Día 6

21

Reasignación y redeclaración

22

Modo estricto

Día 7

23

Debugging y manejo de errores

24

Programación funcional

Quiz: Día 7

Día 8

25

Closures

26

Playground - Crea una calculadora con closures

27

Higher order functions

28

Playground - Crea tu propio método map

Día 9

29

ECMAScript

30

TC39

Quiz: Día 9

Día 10 - Checkpoint

31

ES6

32

ES7

33

Playground - Task planner

Día 11

34

Asincronismo

35

Playground - Promesas

36

Manejando el asincronismo

37

Playground - Resuelve el callback hell usando promesas

38

Playground - Resuelve el callback hell usando async/await

Día 12

39

Arrays a profundidad

40

Métodos de arrays: Every, Find y findIndex

41

Playground - Válida el formulario

Día 13

42

Métodos de arrays: Includes, Join y concat

43

Playground - agrupa los productos

44

Métodos de arrays: Flat y FlatMap

45

Playground - Encuentra la ubicación del valor buscado

Día 14

46

Mutable functions

47

Playground - Modifica una lista de compras

48

Métodos de arrays: sort

49

Playground - Ordena los productos

Día 15 - Checkpoint

50

Playground - Sistema de reservaciones de un hotel

Día 16

51

Programación orientada a objetos en JavaScript

52

Objetos literales

53

Playground - Congela el objeto recursivamente

Día 17

54

Prototipos en JavaScript

55

Playground - Modifica el prototype de los arrays

56

Playground - Crea un auto usando clases

Día 18

57

Abstracción en JavaScript

58

Playground - Sistema de carrito de compras

59

Encapsulamiento en JavaScript

60

Playground - Encapsula datos de los usuarios

Día 19

61

Herencia en JavaScript

62

Playground - Jerarquía de animales

63

Polimorfismo en JavaScript

64

Playground - Sistema de pagos

Día 20 - Checkpoint

65

Playground - Agenda de vuelos

Día 21

66

Patrones de diseño

67

Sinlgeton y Factory pattern en JavaScript

68

Playground - Implementa singleton en un chat

Día 22

69

Adapter y Decorator pattern en JavaScript

70

Playground - Personaliza productos de una tienda

71

Builder y Protype pattern en JavaScript

72

Playground - Mejora el código usando builder pattern

Día 23

73

Facade y proxy pattern en JavaScript

74

Playground - Proxy en servicio de mensajería

75

Chain of responsability y Observer pattern en JavaScript

76

Playground - Implementación de Observador en Newsletter

Día 24 - Checkpoint

77

Playground - Crea un task manager con patrones de diseño

Día 25

78

Estructuras de datos en JavaScript

79

Playground - Crea tu propia implementación de un array

80

Hash tables en JavaScript

81

Playground - Implementación de una HashTable para Contactos

Día 26

82

Set en JavaScript

83

Playground - Remueve duplicados de una lista

84

Maps en JavaScript

85

Playground - Crea un organizador de tareas

Día 27

86

Singly Linked List en JavaScript

87

Playground - Agrega métodos a la singly linked list

88

Playground - Implementación de una singly linked list

Día 28

89

Stacks en JavaScript

90

Playground - Crea un stack para una playlist

Día 29

91

Queues en JavaScript

92

Playground - Crea una cola de emails

Día 30

93

¡Lo lograste!

Live Class

94

30 días de JS con Juan DC

95

30 días de JS con Nicobytes

96

30 días de JS con GNDX

97

30 días de JS con LeoCode

98

30 días de JS con Teffcode

99

Sesión: Cierre de los 30 días de JavaScript

No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Playground - Crea un task manager con patrones de diseño

77/99

Aportes 15

Preguntas 0

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Escudo Anti-Spoilers

Authorization.js

export class Authorization {
  checkAuthorization(user, task) {
    const isAutorized = task.users.some((task_user) => task_user == user);
    if (isAutorized) {
      return true;
    }
    throw new Error("No autorizado");
  }
}

TaskBuilder.js (Version que a mi parecer deberia ser)

A mi parecer para ingresar los campos de deadline y priority se debe hacer uso de una instancia TaskDecorator, ya que por defecto la instancia Task está preparada para recibir solamente el id y description mientras que con TaskDecorator añade más funcionalidades (Justamente la funcion de este sistema de diseño)

import { Task } from "./exercise";
import { TaskDecorator } from "./TaskDecorator";

export class TaskBuilder {
   constructor() {
      this.id = undefined;
      this.description = undefined;
      this.completed = undefined;
      this.users = undefined;
      this.deadline = undefined;
      this.priority = undefined;
   }

   setId(id) {
      this.id = id;
   }

   setDescription(description) {
      this.description = description;
   }

   setCompleted(completed) {
      this.completed = completed;
   }

   setUsers(users) {
      this.users = users;
   }

   setDeadline(deadline) {
      this.deadline = deadline;
   }

   setPriority(priority) {
      this.priority = priority;
   }

   build() {
      const task = new Task(this.id, this.description);
      this.users.forEach((user) => task.assignUser(user));
      if (this.deadline && this.priority) {
        const taskDecorator = new TaskDecorator(task, {
          deadline: this.deadline,
          priority: this.priority,
        });
        return taskDecorator;
      }
   }
}

TaskBuilder.js (Con la que apruebas el playground)

Aqui simplemente agrega atributos que no estan presentes en una instancia de Task

import { Task } from "./exercise";

export class TaskBuilder {
   constructor() {
      this.id = undefined;
      this.description = undefined;
      this.completed = undefined;
      this.users = undefined;
      this.deadline = undefined;
      this.priority = undefined;
   }

   setId(id) {
      this.id = id;
   }

   setDescription(description) {
      this.description = description;
   }

   setCompleted(completed) {
      this.completed = completed;
   }

   setUsers(users) {
      this.users = users;
   }

   setDeadline(deadline) {
      this.deadline = deadline;
   }

   setPriority(priority) {
      this.priority = priority;
   }

   build() {
      const task = new Task(this.id, this.description);
      this.users.forEach((user) => task.assignUser(user));
      task.deadline = this.deadline
      task.priority = this.priority
      return task;
   }
}

TaskDecorator.js

export class TaskDecorator {
   constructor(task, options) {
      this.task = task;
      this.priority = "";
      this.deadline = "";
      this.checkDeadlineFormat(options.deadline);
      this.checkPriority(options.priority);
   }

   checkDeadlineFormat(deadline) {
      const regex = /\d{4}-\d{2}-\d{2}/;
      const isValidFormat = regex.test(deadline);
      if (isValidFormat) {
         this.deadline = deadline;
      } else {
         this.deadline = "2023-04-19";
      }
   }

   checkPriority(priority) {
      const options = ["high", "medium", "low", "alta", "media", "baja"];
      const isValidPriority = options.some(
         (option) => option == priority.toLowerCase()
      );
      if (isValidPriority) {
         this.priority = priority.toLowerCase();
      } else {
         this.priority = "baja";
      }
   }

   assignUser(user) {
      if (user instanceof User) {
         this.task.assignUser(user);
      } else {
         throw new Error("Debe pasar como parametro una instancia de User");
      }
   }

   completeTask() {
      this.task.completeTask();
   }

   notifyUsers() {
      this.task.notifyUsers();
   }
}

TaskManager.js

import { Task } from "./exercise";

export class TaskManager {
   constructor() {
      this.tasks = [];
   }

   static getInstance() {
      if (!TaskManager.instance) {
         TaskManager.instance = new TaskManager();
      }
      return TaskManager.instance;
   }

   addTask(task) {
      if (task instanceof Task) {
         this.tasks.push(task);
      } else {
         throw new Error("Debe pasar como parámetro una instancia de tarea");
      }
   }

   getTasks() {
      return this.tasks;
   }

   getTaskById(id) {
      const task = this.tasks.find((task) => task.id == id);
      return task ? task : null;
   }
}
export class User {
   constructor(name) {
      this.name = name;
   }

   notify(task) {
      return `La tarea ${task.id} fue completada`;
   }
}

exercise.js

import { User } from "./User"

export class Task {
   constructor(id, description) {
      this.id = id;
      this.description = description;
      this.completed = false;
      this.users = [];
   }

   assignUser(user) {
      if (user instanceof User) {
         this.users.push(user);
      } else {
         throw new Error("Debe pasar como parametro una instancia de User");
      }
   }

   completeTask() {
      this.completed = true;
      this.notifyUsers();
   }

   notifyUsers() {
      this.users.forEach((user) => user.notify(this));
   }
}

Mi solución:
.

  • Tuve problemas con la clase TaskBuilder porque no me quedaba claro si, al tener los datos priority y deadline, debía quedar con la misma estructura de la clase Task o TaskDecorator.
  • Para la clase Task, en el método assignUser() no se menciona que se debe hacer la verificación de que el usuario es una instancia de la clase User. Y en el enunciado no se muestra que reciba parámetros (aunque esto es bastante obvio).
  • Hay un par de typos en el enunciado.

.
.
.
.
.

Authorization.js

export class Authorization {
  checkAuthorization(user, task) {
    if (task.users.includes(user)) {
      task.completeTask()
    } else {
      throw new Error("No autorizado")
    }
      
  }
}

TaskBuilder.js

import { Task } from "./exercise";

export class TaskBuilder {
  constructor() {
     this.task = new Task(0, "")
  }

  setId(id) {
     this.task.id = id
     return this
  }

  setDescription(description) {
     this.task.description = description
     return this
  }

  setCompleted(completed) {
     this.task.completed = completed
     return this
  }

  setUsers(users) {
     this.task.users.push(...users)
     return this
  }

  setDeadline(deadline) {
     this.task.deadline = deadline
     return this
  }

  setPriority(priority) {
     this.task.priority = priority
     return this
  }

  build() {
     return this.task
  }
}

TaskDecorator.js

export class TaskDecorator {
  constructor(task, options) {
     this.task = task
     this.deadline = options.deadline
     this.priority = options.priority 
  }

  assignUser(user) {
     this.task.assignUser(user)
  }

  completeTask() {
     this.task.completeTask(
  }

  notifyUsers() {
     this.task.notifyUsers(this)
  }
}

TaskManager.js

import { Task } from "./exercise";

export class TaskManager {
   constructor() {
  }

  static getInstance() {
     if (!TaskManager.instance) {
        TaskManager.tasks = []
        TaskManager.instance = new TaskManager()
     }
     return TaskManager.instance
  }

  addTask(task){
     if (task instanceof Task) { TaskManager.tasks.push(task) }
  }

  getTasks(){
     return TaskManager.tasks
  }

  getTaskById(id){
     return TaskManager.tasks.find(task => task.id === id) || null
  }
}

User.js

export class User {
  constructor(name) {
     this.name = name
  }

  notify(task) {
     console.log(`${this.name} ha sido notificado de que la tarea ${task.id} se completó`)
  }
}

exercise.js

import { User } from "./User"

export class Task{
  constructor(id, description){
     this.id = id
     this.description = description
     this.completed = false
     this.users = []
  }

  assignUser(user){
   if (user instanceof User) {
      this.users.push(user)
   } else {
      throw new Error("El usuario no existe")
   }
   
  }

  completeTask() {
     this.completed = true
     this.notifyUsers()
  }

  notifyUsers() {
     this.users.forEach(user => user.notify(this))
  }
}
Es necesario hacer este comentario, porque el tiempo que se pierde tratando de resolver un posible "error" que tenemos en nuestro código, cuando realmente el error proviene de una documentación que no aclara todos los requerimientos del ejercicio es extremadamente molesto. Por favor equipo Platzi e instructor, se requiere que este curso completo se verifique, documentaciones, ejercicios, etc. Es muy frecuente la presencia de errores en todo. Esto lo hago como recomendación porque imagínense si para uno es molesto que tiene experiencia en el area, que ha de ser para una persona que esta iniciando en el mundo del desarrollo. Esto es lo único negativo que puedo mencionar del curso, mala documentación en muchos casos. 🫤 mi solución..... ![](https://static.platzi.com/media/user_upload/code-4385d60b-f2e5-47a7-9faa-a48ee8bcdbfd.jpg)

Hola comparto la solución:













Authorization.js

export class Authorization {
  checkAuthorization(user, task) {
    if (!task.users.includes(user)) {
      throw new Error("No autorizado");
    }
  }
}

💚Mi solución 💚

🛡️MURO ANTI-SPOILERS🛡️

exercise.js (Task)

import { User } from "./User"

export class Task{
   constructor(id, description) {
      this.id = id
      this.description = description
      this.completed = false
      this.users = []
   }

   assignUser(user) {
      if (!(user instanceof User))
         throw new Error("Not a user")
      this.users.push(user)
   }

   completeTask() {
      this.completed = true
      this.notifyUsers()
   }

   notifyUsers() {
      this.users.forEach(user => user.notify(this))
   }
}

User.js

export class User {
   constructor(name) {
      this.name = name
   }

   notify(task) {
     console.log(`El usuario ${name} ha sido notificado de que "${task.id}:${task.description}" ha sido completada.`)
  }
}

Authorization.js

export class Authorization {
  checkAuthorization(user, task) {
    let indexOfUser = task.users.indexOf(user)
    if (indexOfUser === -1)
      throw new Error('No autorizado')
  }
}

TaskDecorator.js

export class TaskDecorator {
   constructor(task, options) {
      this.task = task
      this.deadline = options.deadline
      this.priority = options.priority
   }

   assignUser(user) {
      this.task.assignUser(user)
   }

   completeTask() {
      this.task.completeTask()
   }

   notifyUsers() {
      this.task.notifyUsers()
   }
}

TaskBuilder.js

import { Task } from "./exercise";

export class TaskBuilder {
   constructor() {
      this.task = new Task('', '')
   }

   setId(id) {
      this.task.id = id
      return this.task
   }

   setDescription(description) {
      this.task.description = description
      return this.task
   }

  setCompleted(completed) {
     this.task.completed = completed
     return this.task
  }

   setUsers(users) {
      this.task.users = users
      return this.task
   }

   setDeadline(deadline) {
      this.task.deadline = deadline
      return this.task
   }

   setPriority(priority) {
      this.task.priority = priority
      return this.task
   }

   build() {
      return this.task
   }
}

TaskManager.js

import { Task } from "./exercise";

export class TaskManager {
   constructor() {
      if (!TaskManager.instance) {
         this.tasks = []
         TaskManager.instance = Object.freeze(this)
      }
      return TaskManager.instance
   }

   static getInstance() {
      return new TaskManager()
   }

   addTask(task) {
      if (!(task instanceof Task))
         throw new Error("Not a Task")
      this.tasks.push(task)
   }

   getTasks() {
      return this.tasks
   }

   getTaskById(id) {
      return this.tasks.find(task => task.id === id) || null
   }
}

Algo difícil, pero esta hecho:
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

exercise.js

import { User } from "./User"

export class Task{
  constructor(id, description) {
     // Tu código aquí 👈
      this.id = id;
      this.description = description;
      this.completed = false;
      this.users = [];
   }

   assignUser(user) {
      // Tu código aquí 👈
      if (!(user instanceof User))
         throw new Error("No existe el usuario a registrar");      
      this.users.push(user);
   }

   completeTask() {
      // Tu código aquí 👈
      this.completed = true;
      this.notifyUsers();
   }

   notifyUsers() {
      // Tu código aquí 👈
      for (let user of this.users)
         user.notify(this);
   }
}

User.js

export class User {
   constructor(name) {
      // Tu código aquí 👈
      this.name = name;
   }

   notify(task) {
      // Tu código aquí 👈
      return `La tarea ${task.id} fue completada`;
   }
}

TaskManager.js

import { Task } from "./exercise";

export class TaskManager {
  constructor() {
     // Tu código aquí 👈
     this.tasks = [];
   }

   static getInstance() {
      // Tu código aquí 👈
      if (!TaskManager.instance) {
         TaskManager.instance = new TaskManager();
      }
      return TaskManager.instance;
   }

   addTask(task) {
      // Tu código aquí 👈
      if (!(task instanceof Task))
         throw new Error('Tarea no valida');
      this.tasks.push(task);
   }

   getTasks() {
      // Tu código aquí 👈
      return this.tasks;
   }

   getTaskById(id) {
      // Tu código aquí 👈
      if (this.tasks.some(task => task.id === id))
         return this.tasks.find(task => task.id === id);
      else return null;
   }
} 

TaskDecorator.js

export class TaskDecorator {
  constructor(task, options) {
     // Tu código aquí 👈
    this.task = task;
    this.deadline = options.deadline;
    this.priority = options.priority;
  }

  assignUser(user) {
    // Tu código aquí 👈
    if (!(user instanceof User))
       throw new Error('No es una instancia de User');
    this.task.assignUser(user);
  }

  completeTask() {
    // Tu código aquí 👈
    this.task.completeTask();
  }

   notifyUsers() {
     // Tu código aquí 👈
     this.task.notifyUsers(this);
   }
}

TaskBuilder.js

import { Task } from "./exercise";

export class TaskBuilder {
  constructor() {
     // Tu código aquí 👈
      this.id = undefined;
      this.description = '';
      this.completed = undefined;
      this.users = [];
      this.deadline = '';
      this.priority = '';
   }

   setId(id) {
      // Tu código aquí 👈
      this.id = id;
   }

   setDescription(description) {
      // Tu código aquí 👈
      this.description = description;
   }

   setCompleted(completed) {
      // Tu código aquí 👈
      this.completed = completed;
   }

   setUsers(users) {
      // Tu código aquí 👈
      this.users = users;
   }

   setDeadline(deadline) {
      // Tu código aquí 👈
      this.deadline = deadline;
   }

   setPriority(priority) {
      // Tu código aquí 👈
      this.priority = priority;
   }

   build() {
      // Tu código aquí 👈
      const task = new Task(this.id, this.description);
      this.users.forEach(user => task.assignUser(user));
      task.deadline = this.deadline;
      task.priority = this.priority;
      return task;
   }
}

Authorization.js

export class Authorization {
  checkAuthorization(user, task) {
    // Tu código aquí 👈
    if (!task.users.some(tkuser => tkuser === user))
      throw new Error('No autorizado');
    return true;
  }
}


.
.
.
.
.
.

exercise.js:

(OBSERVER)

import { User } from "./User"

//Con patrón OBSERVER -- Observable: 
export class Task{
   constructor(id, description){
     this.id = id;
     this.description = description;
     this.completed = false;
     this.users = [];
   }

   assignUser(user) {
      if (user instanceof User) {
         this.users.push(user);
      } else {
         throw new Error("Usuario no encontrado.")
      }
   }

   completeTask() {
      this.completed = true;
      this.notifyUsers();
   }

   notifyUsers() {
     this.users.forEach(user => user.notify(this));
   }
}

User.js:

(OBSERVER)

//Con patrón OBSERVER -- Observador: 
export class User {
    constructor(name) {
      this.name = name;
    }

    notify(task) {
      console.log(`La tarea ${task.id} fue completada con éxito.`)
    }
}

TaskManager.js:

(SINGLETON)

import { Task } from "./exercise";

//Con patrón SINGLETON:
export class TaskManager {
   constructor() {}

   static getInstance() {
      if (!TaskManager.instance) {
         this.tasks = [];
         TaskManager.instance = new TaskManager();
      }
      return TaskManager.instance;
   }

   addTask(task){
      if (task instanceof Task) {
         TaskManager.tasks.push(task);
      } else {
         throw new Error("Tarea inválida.")
      }
   }

   getTasks(){
      return TaskManager.tasks;
   }

   getTaskById(id) {
      return TaskManager.tasks.find(task => task.id === id) || null
   }
}

TaskDecorator.js:

(DECORATOR)

//Con DECORATOR PATTERN:
export class TaskDecorator {
   constructor(task, options) {
      this.task = task;
      this.deadline = options.deadline;
      this.priority = options.priority;
   }

   assignUser(user) {
      this.task.assignUser(user);
   }

   completeTask() {
      this.task.completeTask();
   }

   notifyUsers() {
      this.task.notifyUsers(this);
   }
}

TaskBuilder.js:

(BUILDER)

import { Task } from "./exercise";

//Con BUILDER PATTERN:
export class TaskBuilder {
   constructor() {
      this.task = new Task(0, "")
   }

   setId(id) {
      this.task.id = id;
      return this;
   }

   setDescription(description) {
      this.task.description = description;
      return this;
   }

   setCompleted(completed) {
      this.task.completed = completed;
      return this;
   }

   setUsers(users) {
      this.task.users.push(...users);
      return this;
   }

   setDeadline(deadline) {
      this.task.deadline = deadline;
      return this;
   }

   setPriority(priority) {
      this.task.priority = priority;
      return this;
   }

   build() {
      return this.task;
   }
}

Authorization.js:

(PROXY)

//Con patrón PROXY:
export class Authorization {
  checkAuthorization(user, task) {
    if (task.users.includes(user)) {
      return true;
    } else {
      throw new Error("No autorizado.")
    }
  }
}

Listo

exercise.js

import { User } from "./User"

export class Task {
   constructor(id, description) {
      this.id = id;
      this.description = description;
      this.completed = false;
      this.users = [];
   }
   assignUser(user) {
      if (user instanceof User) {
         this.users.push(user);
      } else {
         throw new Error("Isnt a user instance");
      }
   }
   completeTask() {
      this.completed = true;
      this.notifyUsers();
   }
   notifyUsers() {
      this.users.forEach(user => user.notify(this));
   }
}

User.js

export class User {
   constructor(name) {
      this.name = name;
   }
   notify(task) {
      return `The task with ID ${task.id} has been completed by ${this.user}`;
   }
}

TaskManager.js

import { Task } from "./exercise";

export class TaskManager {
   
   constructor() { 
      if (!TaskManager.instance) {
         this.tasks = []
         TaskManager.instance = Object.freeze(this)
      }
      return TaskManager.instance
   }
   static getInstance() {
      return new TaskManager()
   }
   addTask(task) {
      if (task instanceof Task) {
         this.tasks.push(task);
      } else {
         throw Error(`Not is a instance of Task`);
      }
   }
   getTasks() {
      return this.tasks;
   }
   getTaskById(id) {
      let search = this.tasks.findIndex(task => task.id === id);
      if (!search) {
         return this.tasks[search];
      }
      return null;
   }
}

TaskDecorator.js

export class TaskDecorator {
   constructor(task, options) {
      this.task = task;
      this.deadline = options.deadline;
      this.priority = options.priority;
   }
   assignUser(user) {
      this.task.assignUser(user);
   }
   completeTask() {
      this.task.completeTask();
   }
   notifyUsers() {
      this.task.notifyUsers();
   }
}

TaskBuilder

import { Task } from "./exercise";

export class TaskBuilder {
   constructor() {
      this.id = '';
      this.description = '';
      this.completed = '';
      this.users = [];
      this.deadline = '';
      this.priority = '';
   }
   setId(id) {
      this.id = id;
      return this;
   }
   setDescription(description) {
      this.description = description;
      return this;
   }
   setCompleted(completed) {
      this.completed = completed;
      return this;
   }
   setUsers(users) {
      users.forEach(user => this.users.push(user));
      return this;
   }
   setDeadline(deadline) {
      this.deadline = deadline;
      return this;
   }
   setPriority(priority) {
      this.priority = priority;
      return this
   }
   build() {
      let task = new Task(
         this.id,
         this.description
      );
      this.users.forEach(user => task.assignUser(user));
      task.completed = this.completed,
      task.deadline = this.deadline;
      task.priority = this.priority;
      return task;
   }
}

Authorization.js

export class Authorization {
  checkAuthorization(user, task) {
    if (task.users.includes(user)) {
      return `Authorized`;
    } else {
      throw Error("Not authorized");
    }
  }
}

🛡️🛡️Escudo anti-spoilers🛡️🛡️
Mi solución al reto:

exercise.js

import { User } from "./User"

export class Task{
   constructor(id, description) {
      this.id = id
      this.description = description
      this.completed = false
      this.users = [];
  }

   assignUser(user) {
      if (!(user instanceof User))
         throw new Error('no es instancia de User')
     this.users.push(user)
  }

  completeTask() {
     this.completed = true;
     this.notifyUsers()
  }

  notifyUsers() {
     for (let user of this.users) {
        user.notify(this)
     }
  }
}

User.js

export class User {
  constructor(name) {
     this.name = name
  }

  notify(task) {
     console.log(`La tarea '${task.name}' fue completada`)
  }
}

TaskManager.js

import { Task } from "./exercise";

export class TaskManager {
   constructor() {
     this.tasks = []
  }

  static getInstance() {
     if (!TaskManager.instance) {
        TaskManager.instance = new TaskManager();
     }
     return TaskManager.instance;
  }

 addTask(task){
   if(!task instanceof Task)
      throw new Error("No es una instancia de Task");
       this.tasks.push(task)
  }

  getTasks(){
     return this.tasks
  }

   getTaskById(id) {
     return this.tasks.find(t => t.id === id) || null;
  }
}

taskDecorator.js

import { Task } from "./exercise.js"

export class TaskDecorator extends Task{

  constructor(task, options) {
     super(task)
     this.task = task
     this.priority = options.priority
     this.deadline = options.deadline
  }

   assignUser(user) {
      console.log('Asignando usuario...')
      this.task.assignUser(user)
   
  }

   completeTask() {
     console.log('Marcando la tarea como completada...')
     this.task.completeTask()
  }

   notifyUsers() {
     console.log('Notificando a todos los usuarios...')
     this.task.notifyUsers()
  }
}

TaskBuilder.js

import { Task } from "./exercise";

export class TaskBuilder extends Task{
   constructor(task) {
      super(task)
   }

   setId(id) {
      this.id = id;
      return this
   }

   setDescription(description) {
      this.description = description;
      return this
   }

   setCompleted(completed) {
      this.completed = completed;
      return this
   }

   setUsers(users) {
      this.users = users;
      return this
   }

   setDeadline(deadline) {
      this.deadline = deadline;
      return this
   }

   setPriority(priority) {
      this.priority = priority;
      return this
   }

   build() {
      return this
   }
}

Authorization.js

export class Authorization {
  checkAuthorization(user, task) {
    let exist = task.users.filter(u => user === u).length > 0
    if (!exist) throw new Error('Usuario no autorizado')
    return exist
  }
}

MI SOLUCION 💪
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
class Task

import { User } from "./User"

export class Task {
  constructor(id, description){
     this.id = id;
     this.description = description;
     this.completed = false;
     this.users = [];
  }

  assignUser(user){
     if (user instanceof User) {
        this.users.push(user);
     } else {
        throw new Error("user no es una instancia de User");
     }
  }

  completeTask() {
     this.completed = true;
     this.notifyUsers();
  }

   notifyUsers() {
      this.users.forEach(user => user.notify(this));
  }
}

class Authorization

export class Authorization {
  checkAuthorization(user, task) {
    const isAuthorized = task.users.some(taskUser => taskUser === user);
    if (isAuthorized) return true;
    throw new Error("No autorizado");;
  }
}

class TaskDecorator

export class TaskDecorator {
  constructor(task, options) {
     this.task = task;
     this.priority = "";
     this.deadline = "";
     this.checkDeadlineFormat(options.deadline);
     this.checkPriority(options.priority);
  }

   checkDeadlineFormat(deadline) {
      const regex = /\d{4}-\d{2}-\d{2}/;
      const isValidFormat = regex.test(deadline);
      if (isValidFormat) {
         this.deadline = deadline;
      } else {
         this.deadline = "2023-04-30";
      }
   }

   checkPriority(priority) {
      const options = ["high", "medium", "low", "alta", "media", "baja"];
      const isValidPriority = options.some(option =>
         option == priority.toLowerCase()
      );
      if (isValidPriority) {
         this.priority = priority.toLowerCase();
      } else {
         throw new Error("priority incorrecta");
      }
   }

  assignUser(user) {
     this.task.assignUser(user);
  }

  completeTask() {
     this.task.completeTask();
  }

  notifyUsers() {
     this.task.notifyUsers();
  }
}

class TaskBuilder

import { Task } from "./exercise";

export class TaskBuilder {
  constructor() {
     this.id = undefined;
     this.description = undefined;
     this.completed = undefined;
     this.users = undefined;
     this.deadline = undefined;
     this.priority = undefined;
  }

  setId(id) {
     this.id = id;
  }

  setDescription(description) {
     this.description = description;
  }

  setCompleted(completed) {
     this.completed = completed;
  }

  setUsers(users) {
     this.users = users;
  }

  setDeadline(deadline) {
     this.deadline = deadline;
  }

  setPriority(priority) {
     this.priority = priority;
  }

  build() {
     const task = new Task(this.id, this.description);
     this.users.forEach(user => task.assignUser(user));
     task.deadline = this.deadline
     task.priority = this.priority
     return task;
  }
}

class TaskManager

import { Task } from "./exercise";

export class TaskManager {
  constructor() {
     this.tasks = [];
  }

  static getInstance() {
     if(!TaskManager.instance) TaskManager.instance = new TaskManager();
     return TaskManager.instance;
  }

  addTask(task){
     if (task instanceof Task) {
        this.tasks.push(task);
     } else {
        throw new Error("task incorrecta");
     }
  }

  getTasks(){
     return this.tasks;
  }

  getTaskById(id){
     const task = this.tasks.find(task => task.id == id);
     return task ? task : null;
  }
}

Aqui voy:

Autorization:

export class Authorization {
  checkAuthorization(user, task) {
    if (task.users.includes(user)) {
      return 'Autorizado'
    }
    else {
      throw new Error('No autorizado')
    }
  }
}

TaskBuilder:

import { Task } from "./exercise.js";

export class TaskBuilder extends Task {
   constructor(task) {
      super(task)
   }

   setId(id) {
      super.id = id
      return this
   }

   setDescription(description) {
      super.description = description
      return this
   }

   setCompleted(completed) {
      super.completed = completed
      return this
   }

   setUsers(users) {
      for (let user in users) {
      super.assignUser(users[user])
      }
      return this
   }

   setDeadline(deadline) {
      super.deadline = deadline
      return this
   }

   setPriority(priority) {
      super.priority = priority
      return this
   }

   build() {
      return this
   }
}

TaskDecorator:

export class TaskDecorator {
   constructor(task, options) {
      this.task = task;
      this.deadline = options.deadline;
      this.priority = options.priority;
   }

   assignUser(user) {
      this.task.assignUser(user);
   }

   completeTask() {
      this.task.completeTask();
   }

   notifyUsers() {
      this.task.notifyUsers();
   }
}

TaskManager:

import { Task } from "./exercise.js";

export class TaskManager {
   constructor() {
      if (!TaskManager.instance) {
         this.tasks = []
         TaskManager.instance = Object.freeze(this)
      }
      return TaskManager.instance
   }

   static getInstance() {
      return new TaskManager()
   }

   addTask(task) {
      task instanceof Task ? this.tasks.push(task) : new Error('no es una tarea o no hereda de Task')
   }

   getTasks() {
      return this.tasks
   }

   getTaskById(id) {
      return this.tasks.find(task => task.id === id) || null
   }
}

User:

export class User {
   constructor(name) {
      this.name = name
   }

   notify(task) {
      return `${this.name}: La tarea (${task.description}) ha sido completada`
   }
}

exercise:

import { User } from "./User.js"

export class Task {
   constructor(id, description) {
      this.id = id
      this.description = description
      this.users = []
      this.completed = false
   }

   assignUser(user) {
      if (user instanceof User) this.users.push(user) 
     else throw new Error('no es user')
   }

   completeTask() {
      this.completed = true
      return this.notifyUsers()
   }

   notifyUsers() {
      const responses = []
      this.users.forEach(user => responses.push(user.notify(this)))
      return responses.join('\n')
   }
}

Solución… 😄
.
.
.
.

.
.
Authorization.js:
.

export class Authorization {
  checkAuthorization(user, task) {
    const authorized = task.users.includes(user);
    if (authorized) {
      return true;
    }
    throw new Error("El usuario no está autorizado para esta tarea");
  }
}

.
TaskBuilder.js:
.

import { Task } from "./exercise";

export class TaskBuilder {
   constructor() {
      this.task = new Task();
   }

   setId(id) {
      this.task.id = id;
      return this;
   }

   setDescription(description) {
      this.task.description = description;
      return this;
   }

   setCompleted(completed) {
      this.task.completed = completed;
      return this;
   }

   setUsers(users) {
      this.task.users = users;
      return this;
   }

   setDeadline(deadline) {
      this.task.deadline = deadline;
      return this;
   }

   setPriority(priority) {
      this.task.priority = priority;
      return this;
   }

   build() {
      return this.task;
   }
}

.
TaskDecorator.js:
.

export class TaskDecorator {
   constructor(task, options) {
      this.task = task;
      this.deadline = options.deadline;
      this.priority = options.priority;
   }

   assignUser(user) {
      this.task.assignUser(user);
   }

   completeTask() {
      this.task.completeTask();
   }

   notifyUsers() {
      this.task.notifyUsers();
   }
}

.
TaskManager.js:
.

import { Task } from "./exercise";

export class TaskManager {
   constructor() {
      this.tasks = [];
   }

   static getInstance() {
      if (!TaskManager.instance) {
         TaskManager.instance = new TaskManager();
      }
      return TaskManager.instance;
   }

   addTask(task) {
      if (task instanceof Task) {
         this.tasks.push(task);
      }
   }

   getTasks() {
      return this.tasks;
   }

   getTaskById(id) {
      return this.tasks.find(task => task.id === id) || null;
   }
}

.
User.js:
.

export class User {
   constructor(name) {
      this.name = name;
   }

   notify(task) {
      return `${this.name} ha sido notificado: la tarea ${task.id} ha sido completada`;
   }
}

.
exercise.js:
.

import { User } from "./User"

export class Task {
   constructor(id, description) {
      this.id = id;
      this.description = description;
      this.completed = false;
      this.users = [];
   }

   assignUser(user) {
      if (user instanceof User) {
         this.users.push(user);
      } else {
         throw new Error("Se debe asignar una instancia de usuario");
      }
   }

   completeTask() {
      this.completed = true;
      this.notifyUsers();
   }

   notifyUsers() {
      this.users.forEach(user => user.notify(this));
   }
}

Definitivamente aun tengo que amueblar el cerebro para trabajar con JS “multicapa retorcida” xD

,
,
,
,
,
,
,

import { Task } from "./exercise";

export class TaskBuilder {
   constructor() {
      this.task = new Task();
   }

   setId(id) {
      this.task.id = id;
      return this;
   }

   setDescription(description) {
      this.task.description = description;
      return this;
   }

   setCompleted(completed) {
      this.task.completed = completed;
      return this;
   }

   setUsers(users) {
      this.task.users = users;
      return this;
   }

   setDeadline(deadline) {
      this.task.deadline = deadline;
      return this;
   }

   setPriority(priority) {
      this.task.priority = priority;
      return this;
   }

   build() {
      return this.task;
   }
}

import { User } from "./User"

export class Task {
   constructor(id, description) {
      this.id = id;
      this.description = description;
      this.completed = false;
      this.users = [];
   }

   assignUser(user) {
    if(!(user instanceof User)){
        throw new Error("No hereda de la clase user")
      }
      this.users.push(user);
   }

   completeTask() {
      this.completed = true;
      this.notifyUsers();
   }

   notifyUsers() {
      this.users.forEach(user => {
         user.notify(this);
      });
   }
}
export class User {
    constructor(name) {
       this.name = name;
    }
 
    notify(task) {
       console.log(`La tarea ${task.description} ha sido completada`);
    }
 }
import { Task } from "./exercise";

export class TaskManager {
   constructor() {
      this.tasks = [];
   }

   static getInstance() {
      if (!TaskManager.instance) {
         TaskManager.instance = new TaskManager();
      }
      return TaskManager.instance;
   }

   addTask(task) {
      if (task instanceof Task) {
         this.tasks.push(task);
      } else {
         throw new Error('La tarea no es válida');
      }
   }

   getTasks() {
      return this.tasks;
   }

   getTaskById(id) {
      return this.tasks.find(task => task.id === id) || null;
   }
}
export class TaskDecorator {
    constructor(task, options) {
      this.task = task
      this.deadline = options.deadline;
      this.priority = options.priority;
    }
  
    assignUser(user) {
      this.task.assignUser(user);
    }
  
    completeTask() {
      this.task.completeTask();
    }
  
    notifyUsers() {
      this.task.notifyUsers();
    }
  }

Solución

Authorization

export class Authorization {
  checkAuthorization(user, task) {
    // Tu código aquí 👈
    const isAuthenticated =
      task.users.findIndex((taskUser) => taskUser.name === user.name) !== -1;

    if (!isAuthenticated) throw Error("Not authorized");

    console.log(`User: ${user.name} is authorized`);
    task.completeTask();
  }
}

exercise

import { User } from "./User.mjs";

export class Task {
  constructor(id, description) {
    // Tu código aquí 👈
    this.id = id;
    this.description = description;
    this.completed = false;
    this.users = [];
  }

  assignUser(user) {
    // Tu código aquí
    if (user instanceof User) {
      this.users.push(user);
      return this.users;
    }
  }

  completeTask() {
    // Tu código aquí 👈
    if (this.users.length === 0) {
      console.log("Cannot complete task, no user assigned to this task");
      return;
    }
    this.completed = true;
    this.notifyUsers();
  }

  notifyUsers() {
    // Tu código aquí 👈
    this.users.forEach((user) => user.notify(this));
  }
}

TaskBuilder

import { Task } from "./excercise.mjs";

export class TaskBuilder {
  constructor() {
    // Tu código aquí 👈
    this.id = 0;
    this.description = "";
    this.completed = false;
    this.users = [];
    this.deadline = null;
    this.priority = null;
  }

  setId(id) {
    // Tu código aquí 👈
    this.id = id;
    return this;
  }

  setDescription(description) {
    // Tu código aquí 👈
    this.description = description;
    return this;
  }

  setCompleted(completed) {
    // Tu código aquí 👈
    this.completed = completed;
    return this;
  }

  setUsers(users) {
    // Tu código aquí 👈
    this.users = users;
    return this;
  }

  setDeadline(deadline) {
    // Tu código aquí 👈
    this.deadline = deadline;
    return this;
  }

  setPriority(priority) {
    // Tu código aquí 👈
    this.priority = priority;
    return this;
  }

  build() {
    // Tu código aquí 👈
    const task = new Task(this.id, this.description);
    this.users.forEach((user) => task.assignUser(user));
    task.priority = this.priority;
    task.deadline = this.deadline;
    return task;
  }
}

TaskDecorator

export class TaskDecorator {
  constructor(task, options) {
    // Tu código aquí 👈
    this.task = task;
    this.deadline = options.deadline;
    this.priority = options.priority;
  }

  assignUser(user) {
    // Tu código aquí 👈
    this.task.assignUser(user);
  }

  completeTask() {
    // Tu código aquí 👈
    this.task.completeTask();
  }

  notifyUsers() {
    // Tu código aquí 👈
    this.task.notifyUsers();
  }
}

TaskManager

import { Task } from "./excercise.mjs";

export class TaskManager {
  static instance = null;

  constructor() {
    // Tu código aquí 👈
    this.tasks = [];
  }

  static getInstance() {
    // Tu código aquí 👈
    if (TaskManager.instance === null) {
      TaskManager.instance = new TaskManager();
    }
    return TaskManager.instance;
  }

  addTask(task) {
    // Tu código aquí 👈
    if (!(task instanceof Task)) return;

    if (
      this.tasks.findIndex((managerTask) => managerTask.id === task.id) !== -1
    ) {
      throw Error("This task already exists in the Task manager");
    }

    this.tasks.push(task);
    console.log(`Task added: ${task.description}`);
  }

  getTasks() {
    // Tu código aquí
    if (this.tasks.length === 0) {
      console.log("The tasks list is empty");
      return;
    }

    console.log(this.tasks);
    return this.tasks;
  }

  getTaskById(id) {
    // Tu código aquí 👈
    const task = this.tasks.find((task) => task.id === id);
    console.log(task);
    return task ? task : null;
  }
}

User

export class User {
  constructor(name) {
    // Tu código aquí 👈
    this.name = name;
  }

  notify(task) {
    // Tu código aquí 👈
    console.log(`The task: ${task?.description} is completed.`);
  }
}

Se logró 😄, sude la gota amarga pero se logró:
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

Authorization.js

export class Authorization {
  checkAuthorization(user, task) {
    if (task.users.includes(user)) {
      task.completeTask();
    } else {
      throw new Error("El usuario no esta autorizado para completar la tarea");
    }
  }
}

TaskBuilder.js

import { Task } from "./exercise";

export class TaskBuilder {
   constructor() {
      this.task = new Task();
   }

   setId(id) {
      this.task.id = id;
      return this;
   }

   setDescription(description) {
      this.task.description = description;
      return this;
   }

   setCompleted(completed) {
      this.task.completed = completed;
      return this;
   }

   setUsers(users) {
      this.task.users = users;
      return this;
   }

   setDeadline(deadline) {
      this.task.deadline = deadline;
      return this;
   }

   setPriority(priority) {
      this.task.priority = priority;
      return this;
   }

   build() {
      return this.task
   }
}

TaskDecorator.js

export class TaskDecorator {
   constructor(task, options) {
      this.task = task;
      this.deadline = options.deadline;
      this.priority = options.priority;
   }

   assignUser(user) {
      this.task.assignUser(user);
   }

   completeTask() {
      this.task.completeTask();
   }

   notifyUsers() {
      this.users.forEach(user => {
         user.notify(this)
      })
   }
}


TaskManager.js

import { Task } from "./exercise";

export class TaskManager {
   constructor() {
      this.tasks = [];
   }

   static getInstance() {
      if (!TaskManager.instance) {
         TaskManager.instance = new TaskManager();
      }
      return TaskManager.instance;
   }

   addTask(task) {
      if (task instanceof Task) {
         this.tasks.push(task);
      } else {
         throw new Error(`La tarea no es valida`)
      }
   }

   getTasks() {
      return this.tasks;
   }

   getTaskById(id) {
      let foundTask = this.tasks.find(task => task.id === id);
      if (foundTask) {
         return foundTask;
      } else {
         return null;
      }
   }
}

User.js

export class User {
   constructor(name) {
      this.name = name;
   }

   notify(task) {
      return `La tarea ${task} ha sido completada`;
   }
}

excercise.js

import { User } from "./User"

export class Task {
   constructor(id, description) {
      this.users = [];
      this.id = id;
      this.description = description;
      this.completed = false;
   }

   assignUser(user) {
      if (user instanceof User) {
         this.users.push(user);
      } else {
         throw new Error(`No se puede asignar si no es de la clase User`)
      }
   }

   completeTask() {
      this.completed = true;
      this.notifyUsers();
   }

   notifyUsers() {
      this.users.forEach(user => {
         user.notify(this)
      })
   }
}
undefined