DEV Community

张一凡
张一凡

Posted on

Practical Application of easy-model in Task Management Apps

Task management applications like Todoist or Trello need to handle task states, list management, user collaboration, and other complex logic. easy-model's domain-driven design can clearly organize these functional modules. This article demonstrates easy-model's application in task management through real examples.

Task Model Design

Tasks are core domain entities. We create TaskModel to encapsulate task logic:

import { useModel } from "easy-model";

class TaskModel {
  task = {
    id: "",
    title: "",
    description: "",
    completed: false,
    priority: "medium" as "low" | "medium" | "high",
    dueDate: null as Date | null
  };

  constructor(initialTask: typeof this.task) {
    this.task = initialTask;
  }

  toggleComplete() {
    this.task.completed = !this.task.completed;
  }

  updatePriority(newPriority: typeof this.task.priority) {
    this.task.priority = newPriority;
  }

  isOverdue() {
    return this.task.dueDate && new Date() > this.task.dueDate && !this.task.completed;
  }
}

function TaskItem({ taskId }: { taskId: string }) {
  const taskModel = useModel(TaskModel, [{
    id: taskId,
    title: "Complete project report",
    description: "Write Q1 quarterly report",
    completed: false,
    priority: "high",
    dueDate: new Date("2024-03-20")
  }]);

  return (
    <div>
      <h4>{taskModel.task.title}</h4>
      <p>{taskModel.task.description}</p>
      <p>Priority: {taskModel.task.priority}</p>
      <p>Status: {taskModel.task.completed ? "Completed" : "In Progress"}</p>
      {taskModel.isOverdue() && <p style={{color: 'red'}}>Overdue</p>}
      <button onClick={() => taskModel.toggleComplete()}>
        {taskModel.task.completed ? "Mark Incomplete" : "Mark Complete"}
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

The model encapsulates task business rules like overdue detection and status toggling.

Task List Management

Task lists need shared state across components:

import { provide, useInstance } from "easy-model";

class TaskListModel {
  tasks: TaskModel[] = [];
  listId: string;

  constructor(listId: string) {
    this.listId = listId;
  }

  addTask(task: InstanceType<typeof TaskModel>) {
    this.tasks.push(task);
  }

  getCompletedCount() {
    return this.tasks.filter(t => t.task.completed).length;
  }

  getPendingTasks() {
    return this.tasks.filter(t => !t.task.completed);
  }
}

const TaskListProvider = provide(TaskListModel);

function TaskList({ listId }: { listId: string }) {
  const taskList = useInstance(TaskListProvider(listId));

  return (
    <div>
      <h3>Task List ({taskList.tasks.length} tasks)</h3>
      <p>Completed: {taskList.getCompletedCount()}</p>
      <p>Pending: {taskList.getPendingTasks().length}</p>
      {taskList.tasks.map(task => (
        <TaskItem key={task.task.id} taskId={task.task.id} />
      ))}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

provide ensures list state is shared between components.

Collaboration Features

Multi-user collaboration involves async operations:

import { loader, useLoader, useModel } from "easy-model";

class CollaborationModel {
  collaborators: string[] = [];
  projectId: string;

  constructor(projectId: string) {
    this.projectId = projectId;
  }

  @loader.load(true)
  async inviteUser(email: string) {
    // Simulate API
    await new Promise(resolve => setTimeout(resolve, 1500));
    this.collaborators.push(email);
  }
}

function InviteForm({ projectId }: { projectId: string }) {
  const collabModel = useModel(CollaborationModel, [projectId]);
  const { isLoading } = useLoader();

  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      const email = (e.target as any).email.value;
      collabModel.inviteUser(email);
    }}>
      <input name="email" placeholder="User email" />
      <button type="submit" disabled={isLoading(collabModel.inviteUser)}>
        {isLoading(collabModel.inviteUser) ? "Inviting..." : "Invite User"}
      </button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

Async invitation handling with automatic loading state management.

Testability Assurance

Model classes support comprehensive testing:

describe("TaskModel", () => {
  it("should toggle completion", () => {
    const model = new TaskModel({
      id: "1",
      title: "Test",
      description: "",
      completed: false,
      priority: "medium",
      dueDate: null,
    });
    model.toggleComplete();
    expect(model.task.completed).toBe(true);
  });

  it("should detect overdue", () => {
    const pastDate = new Date("2020-01-01");
    const model = new TaskModel({
      id: "1",
      title: "Test",
      description: "",
      completed: false,
      priority: "medium",
      dueDate: pastDate,
    });
    expect(model.isOverdue()).toBe(true);
  });
});
Enter fullscreen mode Exit fullscreen mode

Tests cover business logic, ensuring functionality correctness.

Conclusion

easy-model excels in task management applications: clear domain modeling, state sharing, async processing. Combined with test-driven development, improves app quality. Experience easy-model for more efficient task management!

Project repo: GitHub

Top comments (0)