diff --git a/data/tasks.txt b/data/tasks.txt new file mode 100644 index 000000000..e515d27d8 --- /dev/null +++ b/data/tasks.txt @@ -0,0 +1,7 @@ +T | 0 | read +D | 0 | submit assignment | 2025-04-01 1800 +E | 0 | group meeting | 2025-04-02 1000 | 2025-04-02 1200 +T | 0 | read book +T | 0 | read book +D | 0 | return book | 2025-04-01 1800 +E | 0 | group project meeting | 2025-04-02 1400 | 2025-04-02 1600 diff --git a/docs/README.md b/docs/README.md index 47b9f984f..dc50d4d66 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,30 +1,159 @@ -# Duke User Guide +# Eddie Task Manager! +``` + _____ ____ ____ ___ _____ +| ____|| _ \ | _ \ |_ _|| ____| +| |_ | | | || | | | | | | |_ +| _| | | | || | | | | | | |_| +| |___ | |_| || |_| | | | | |___ +|_____||____/ |____/ |___||_____| -// Update the title above to match the actual product name +Eddie: Welcome to your personal task manager! +I can help you manage different types of tasks. +``` -// Product screenshot goes here +# Eddie User Guide -// Product intro goes here +## Introduction -## Adding deadlines +**Eddie** is a command-line based chatbot designed to help you organize your tasks. Whether it’s a **Todo**, a **Deadline**, or an **Event** Eddie has you covered! Eddie understands natural-like commands and can **add, list, delete, mark, unmark, find** and even **remember your tasks between sessions** with **date parsing** and **search** features! -// Describe the action and its outcome. +--- +# Features +## Adding Tasks -// Give examples of usage +### Add ToDo, Deadline, or Event Tasks -Example: `keyword (optional arguments)` +- `todo {description}` – Adds a ToDo task +- `deadline {description} /by {yyyy-MM-dd HHmm}` – Adds a Deadline +- `event {description} /from {yyyy-MM-dd HHmm} /to {yyyy-MM-dd HHmm}` – Adds an Event -// A description of the expected outcome goes here +Example: adding tasks +``` +____________________________________________________________ +You: todo read book +____________________________________________________________ +Eddie: +Got it. I've added this task: + [T][ ] read book +Now you have 1 task in the list. +____________________________________________________________ +``` +``` +____________________________________________________________ +You: deadline return book /by 2025-04-01 1800 +____________________________________________________________ +Eddie: +Got it. I've added this task: + [D][ ] return book (by: Apr 1 2025, 6:00pm) +Now you have 2 tasks in the list. +____________________________________________________________ ``` -expected output ``` +__________________________________________________________________________________ +You: event group project meeting /from 2025-04-02 1400 /to 2025-04-02 1600 +__________________________________________________________________________________ +Eddie: +Got it. I've added this task: + [E][ ] group project meeting (from: Apr 2 2025, 2:00pm to: Apr 2 2025, 4:00pm) +Now you have 3 tasks in the list. +__________________________________________________________________________________ +``` + +--- + +### List All Tasks + +- `list` – Shows all tasks currently tracked. + +#### Example: +``` +__________________________________________________________________________________ +You: list +__________________________________________________________________________________ +Eddie: +Here are the tasks in your list: +1. [T][ ] read book +2. [D][ ] return book (by: Apr 1 2025, 6:00pm) +3. [E][ ] group project meeting (from: Apr 2 2025, 2:00pm to: Apr 2 2025, 4:00pm) +__________________________________________________________________________________ +``` + +--- + +### Mark/Unmark Tasks + +- `mark {task number}` – Mark a task as done +- `unmark {task number}` – Mark a task as not done + +#### Example: +``` +____________________________________________________________ +You: mark 1 +____________________________________________________________ +Eddie: +Yay! Marked as done: + [T][X] read book +Have a Beer!! +____________________________________________________________ +You: unmark 1 +____________________________________________________________ +Eddie: +Oh no! Marked as not done: + [T][ ] read book +____________________________________________________________ +``` + +--- + +### Delete a Task + +- `delete {task number}` – Deletes the selected task from the list. + +#### Example: +``` +____________________________________________________________ +You: delete 2 +____________________________________________________________ +Eddie: +Noted. I've removed this task: + [D][ ] return book (by: Apr 1 2025, 6:00pm) +Now you have 2 tasks in the list. +____________________________________________________________ +``` + +--- + +### Find Tasks + +- `find {keyword}` – Search and display tasks that match the keyword. + +#### Example: +``` +____________________________________________________________ +You: find book +____________________________________________________________ +Eddie: +Here are the tasks matching your list: +1. [T][ ] read book +2. [D][ ] return book (by: Apr 1 2025, 6:00pm) +____________________________________________________________ +``` + +--- + +### Storage + +Eddie saves your task list in a text file at `data/tasks.txt`, and loads it every time you start the app. -## Feature ABC +--- -// Feature details +## How to Use +1. Clone this repo and build the project in your IDE +2. Run `Eddie.java`. +3. Interact with Eddie through the terminal using the supported commands above. +4. Exit using the command `bye`. -## Feature XYZ +--- -// Feature details \ No newline at end of file diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 000000000..bad826676 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-midnight diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334c..000000000 --- a/src/main/java/Duke.java +++ /dev/null @@ -1,10 +0,0 @@ -public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - } -} diff --git a/src/main/java/Eddie.java b/src/main/java/Eddie.java new file mode 100644 index 000000000..4cf23ead2 --- /dev/null +++ b/src/main/java/Eddie.java @@ -0,0 +1,60 @@ +import command.Command; +import command.EddieException; +import parser.Parser; +import storage.Storage; +import tasklist.TaskList; +import ui.Ui; + +/** + * The main class for the Eddie chatbot task manager. + */ +public class Eddie { + private Ui ui; + private Storage storage; + private TaskList tasks; + + /** + * Initializes Eddie with the default storage file path. + */ + public Eddie(String filePath) { + ui = new Ui(); + storage = new Storage(filePath); + + try { + tasks = new TaskList(storage.loadTask()); + } catch (EddieException e) { + ui.showLoadingError(); + tasks = new TaskList(); + } + } + + /** + * Starts the Eddie chatbot. + */ + public void run() { + ui.showStartPage(); + boolean isRunning = true; + + while (isRunning) { + String userInput = ui.readCommand(); + ui.printLine(); + try { + Command command = Parser.parse(userInput); + command.execute(tasks, ui, storage); + if (command.isExit()) { + isRunning = false; + } + } catch (EddieException e) { + ui.showError(e.getMessage()); + } + ui.printLine(); + } + } + + /** + * Entry point for the program. + */ + public static void main(String[] args) { + new Eddie("data/tasks.txt").run(); + } +} diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..372c9c8c8 --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Eddie + diff --git a/src/main/java/command/AddCommand.java b/src/main/java/command/AddCommand.java new file mode 100644 index 000000000..71d0d3701 --- /dev/null +++ b/src/main/java/command/AddCommand.java @@ -0,0 +1,72 @@ +package command; + +import storage.Storage; +import tasklist.TaskList; +import ui.ErrorMessages; +import ui.Ui; +import task.Todo; +import task.Deadline; +import task.Event; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Represents a command to add a new task todo, deadline and event. + */ +public class AddCommand extends Command { + private final String taskDetails; + private final String taskType; + + public AddCommand(String taskDetails, String taskType) { + this.taskDetails = taskDetails; + this.taskType = taskType; + } + + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) throws EddieException { + switch (taskType) { + case "todo": + if (taskDetails.isEmpty()) { + throw new EddieException(ErrorMessages.INVALID_TODO); + } + tasks.addTask(new Todo(taskDetails)); + break; + case "deadline": + String[] deadlineParts = taskDetails.split(" /by ", 2); + if (deadlineParts.length < 2 || deadlineParts[0].trim().isEmpty()) { + throw new EddieException(ErrorMessages.INVALID_DEADLINE); + } + try { + DateTimeFormatter inputFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"); + LocalDateTime by = LocalDateTime.parse(deadlineParts[1].trim(), inputFormat); + tasks.addTask(new Deadline(deadlineParts[0].trim(), by)); + } catch (DateTimeParseException e) { + throw new EddieException(ErrorMessages.INVALID_DEADLINE); + } + break; + case "event": + String[] eventParts = taskDetails.split(" /from ", 2); + if (eventParts.length < 2 || !eventParts[1].contains(" /to ") || eventParts[0].trim().isEmpty()) { + throw new EddieException(ErrorMessages.INVALID_EVENT); + } + String[] timeParts = eventParts[1].split(" /to ", 2); + try { + DateTimeFormatter inputFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"); + LocalDateTime from = LocalDateTime.parse(timeParts[0].trim(), inputFormat); + LocalDateTime to = LocalDateTime.parse(timeParts[1].trim(), inputFormat); + tasks.addTask(new Event(eventParts[0].trim(), from, to)); + } catch (DateTimeParseException e) { + throw new EddieException(ErrorMessages.INVALID_EVENT); + } + break; + default: + throw new EddieException(ErrorMessages.INVALID_COMMAND); + } + + // Show confirm task to user and save tasks + ui.showTaskAdded(tasks.getLastTask(), tasks.size()); + storage.saveTasks(tasks.getAllTasks()); + } +} diff --git a/src/main/java/command/Command.java b/src/main/java/command/Command.java new file mode 100644 index 000000000..964e40e6f --- /dev/null +++ b/src/main/java/command/Command.java @@ -0,0 +1,26 @@ +package command; + +import storage.Storage; +import tasklist.TaskList; +import ui.Ui; + +/** + * Abstract base class for all user commands. + */ +public abstract class Command { + /** + * Executes the command with the provided task list, UI, and storage. + * + * @param tasks The task list to operate on. + * @param ui The UI to interact with the user. + * @param storage The storage to save and load data. + * @throws EddieException for error occurs during command execution. + */ + public abstract void execute(TaskList tasks, Ui ui, Storage storage) throws EddieException; + + public boolean isExit() { + return false; + } +} + + diff --git a/src/main/java/command/DeleteCommand.java b/src/main/java/command/DeleteCommand.java new file mode 100644 index 000000000..3f2ae53a8 --- /dev/null +++ b/src/main/java/command/DeleteCommand.java @@ -0,0 +1,27 @@ +package command; + +import storage.Storage; +import tasklist.TaskList; +import ui.Ui; +import task.Task; + +public class DeleteCommand extends Command { + private String taskIndex; + + public DeleteCommand(String taskIndex) { + this.taskIndex = taskIndex; + } + + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + try { + int index = Integer.parseInt(taskIndex) - 1; + Task removedTask = tasks.removeTask(index); + ui.showTaskDeleted(removedTask, tasks.size()); + storage.saveTasks(tasks.getAllTasks()); + + } catch (NumberFormatException | EddieException e) { + ui.showError("Invalid task number!"); + } + } +} diff --git a/src/main/java/command/EddieException.java b/src/main/java/command/EddieException.java new file mode 100644 index 000000000..156fbfa4b --- /dev/null +++ b/src/main/java/command/EddieException.java @@ -0,0 +1,8 @@ +package command; + +public class EddieException extends Exception { + public EddieException(String message) { + super(message); + } +} + diff --git a/src/main/java/command/ExitCommand.java b/src/main/java/command/ExitCommand.java new file mode 100644 index 000000000..a0e96d65d --- /dev/null +++ b/src/main/java/command/ExitCommand.java @@ -0,0 +1,17 @@ +package command; + +import storage.Storage; +import tasklist.TaskList; +import ui.Ui; + +public class ExitCommand extends Command { + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + ui.showEndPage(); + } + + @Override + public boolean isExit() { + return true; + } +} diff --git a/src/main/java/command/FindCommand.java b/src/main/java/command/FindCommand.java new file mode 100644 index 000000000..8548c3840 --- /dev/null +++ b/src/main/java/command/FindCommand.java @@ -0,0 +1,46 @@ +package command; + +import storage.Storage; +import tasklist.TaskList; +import ui.Ui; +import task.Task; + +import java.util.ArrayList; +import java.util.List; + +/** + * Finds and displays tasks that descriptions contain a given keyword. + */ +public class FindCommand extends Command { + private final String keyword; + + /** + * Constructs a FindCommand with the specified keyword to search for. + * + * @param keyword The keyword used to search task descriptions. + */ + public FindCommand(String keyword) { + this.keyword = keyword; + } + + /** + * Executes the find command by searching task list for tasks that contain the same keyword + * in their description, and displays the matching tasks using the UI. + * + * @param tasks The TaskList containing the user's tasks. + * @param ui The Ui instance to handle user interaction. + * @param storage The Storage instance (not used here, but required by method signature). + */ + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + List matchedTasks = new ArrayList<>(); + + for (Task task : tasks.getAllTasks()) { + if (task.getDescription().toLowerCase().contains(keyword.toLowerCase())) { + matchedTasks.add(task); + } + } + + ui.showMatchingTasks(matchedTasks); + } +} diff --git a/src/main/java/command/ListCommand.java b/src/main/java/command/ListCommand.java new file mode 100644 index 000000000..d595b38a7 --- /dev/null +++ b/src/main/java/command/ListCommand.java @@ -0,0 +1,12 @@ +package command; + +import storage.Storage; +import tasklist.TaskList; +import ui.Ui; + +public class ListCommand extends Command { + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + ui.showTaskList(tasks.getAllTasks()); + } +} diff --git a/src/main/java/command/MarkCommand.java b/src/main/java/command/MarkCommand.java new file mode 100644 index 000000000..418bd6c60 --- /dev/null +++ b/src/main/java/command/MarkCommand.java @@ -0,0 +1,31 @@ +package command; + +import storage.Storage; +import task.Task; +import tasklist.TaskList; +import ui.Ui; + + +public class MarkCommand extends Command { + private String taskIndex; + private boolean isMarking; + + public MarkCommand(String taskIndex, boolean isMarking) { + this.taskIndex = taskIndex; + this.isMarking = isMarking; + } + + @Override + public void execute(TaskList tasks, Ui ui, Storage storage) { + try { + int index = Integer.parseInt(taskIndex) - 1; + Task task = tasks.getTask(index); + tasks.markTask(index, isMarking); + ui.showMarkedStatus(task, isMarking); + storage.saveTasks(tasks.getAllTasks()); + + } catch (NumberFormatException | EddieException e) { + ui.showError("Invalid task number!"); + } + } +} diff --git a/src/main/java/parser/Parser.java b/src/main/java/parser/Parser.java new file mode 100644 index 000000000..bca3de501 --- /dev/null +++ b/src/main/java/parser/Parser.java @@ -0,0 +1,42 @@ +package parser; + +import command.*; +import command.EddieException; + +public class Parser { + /** + * Parses the user input and returns the corresponding command. + * + * @param input The raw user input. + * @return A Command object that represents the user's action. + * @throws EddieException If the input is invalid. + */ + public static Command parse(String input) throws EddieException { + String[] parts = input.trim().split(" ", 2); + String command = parts[0].toLowerCase(); + String arguments = (parts.length > 1) ? parts[1] : ""; + + switch (command) { + case "todo": + return new AddCommand(arguments, "todo"); + case "deadline": + return new AddCommand(arguments, "deadline"); + case "event": + return new AddCommand(arguments, "event"); + case "list": + return new ListCommand(); + case "mark": + return new MarkCommand(arguments, true); + case "unmark": + return new MarkCommand(arguments, false); + case "delete": + return new DeleteCommand(arguments); + case "find": + return new FindCommand(arguments); + case "bye": + return new ExitCommand(); + default: + throw new EddieException("Unknown command! Try: todo, deadline, event, list, mark {num}, unmark {num}, delete {num}, or bye."); + } + } +} diff --git a/src/main/java/storage/Storage.java b/src/main/java/storage/Storage.java new file mode 100644 index 000000000..1f831486a --- /dev/null +++ b/src/main/java/storage/Storage.java @@ -0,0 +1,94 @@ +package storage; + +import task.Task; +import task.Todo; +import task.Deadline; +import task.Event; +import tasklist.TaskList; +import command.EddieException; +import ui.ErrorMessages; + +import java.io.*; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; + +public class Storage { + private final String filePath; + private static final DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"); + + public Storage(String filePath) { + this.filePath = filePath; + } + + public ArrayList loadTask() throws EddieException { + ArrayList tasks = new ArrayList<>(); + File file = new File(filePath); + + if (!file.exists()) { + createFile(); + return tasks; + } + + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + String line; + while ((line = reader.readLine()) != null) { + tasks.add(parseTask(line)); + } + } catch (IOException e) { + throw new EddieException(ErrorMessages.TASK_LOAD_FAILED + e.getMessage()); + } + + return tasks; + } + + public void saveTasks(ArrayList tasks) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) { + for (Task task : tasks) { + writer.write(task.toFileFormat()); + writer.newLine(); + } + } catch (IOException e) { + System.out.println(ErrorMessages.SAVE_TASK_FAILED + e.getMessage()); + } + } + + private Task parseTask(String line) throws EddieException { + try { + String[] parts = line.split(" \\| "); + String type = parts[0]; + boolean isDone = parts[1].equals("1"); + String description = parts[2]; + + switch (type) { + case "T": + return new Todo(description, isDone); + case "D": + LocalDateTime by = LocalDateTime.parse(parts[3], dateTimeFormat); + return new Deadline(description, by, isDone); + case "E": + LocalDateTime from = LocalDateTime.parse(parts[3], dateTimeFormat); + LocalDateTime to = LocalDateTime.parse(parts[4], dateTimeFormat); + return new Event(description, from, to, isDone); + default: + throw new EddieException(ErrorMessages.TASK_TYPE_UNKNOWN + type); + } + } catch (Exception e) { + throw new EddieException(ErrorMessages.TASK_PARSING_ERROR + line); + } + } + + private void createFile() throws EddieException { + File file = new File(filePath); + File parent = file.getParentFile(); + if (!parent.exists() && !parent.mkdirs()) { + throw new EddieException(ErrorMessages.MKDIR_FAILED); + } + + try { + file.createNewFile(); + } catch (IOException e) { + throw new EddieException(ErrorMessages.SAVE_TASK_FAILED + e.getMessage()); + } + } +} diff --git a/src/main/java/task/Deadline.java b/src/main/java/task/Deadline.java new file mode 100644 index 000000000..33d5a2d3d --- /dev/null +++ b/src/main/java/task/Deadline.java @@ -0,0 +1,35 @@ +package task; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class Deadline extends Task { + private final LocalDateTime by; + private static final DateTimeFormatter displayFormat = DateTimeFormatter.ofPattern("MMM d yyyy, h:mma"); + private static final DateTimeFormatter saveFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"); + + public Deadline(String description, LocalDateTime by) { + super(description); + this.by = by; + } + + public Deadline(String description, LocalDateTime by, boolean isDone) { + super(description, isDone); + this.by = by; + } + + @Override + public String getTaskType() { + return "[D]"; + } + + @Override + public String toFileFormat() { + return "D | " + (isDone ? "1" : "0") + " | " + description + " | " + by.format(saveFormat); + } + + @Override + public String toString() { + return super.toString() + " (by: " + by.format(displayFormat) + ")"; + } +} diff --git a/src/main/java/task/Event.java b/src/main/java/task/Event.java new file mode 100644 index 000000000..b8db42d2e --- /dev/null +++ b/src/main/java/task/Event.java @@ -0,0 +1,38 @@ +package task; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class Event extends Task { + private final LocalDateTime from; + private final LocalDateTime to; + private static final DateTimeFormatter displayFormat = DateTimeFormatter.ofPattern("MMM d yyyy, h:mma"); + private static final DateTimeFormatter saveFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm"); + + public Event(String description, LocalDateTime from, LocalDateTime to) { + super(description); + this.from = from; + this.to = to; + } + + public Event(String description, LocalDateTime from, LocalDateTime to, boolean isDone) { + super(description, isDone); + this.from = from; + this.to = to; + } + + @Override + public String getTaskType() { + return "[E]"; + } + + @Override + public String toFileFormat() { + return "E | " + (isDone ? "1" : "0") + " | " + description + " | " + from.format(saveFormat) + " | " + to.format(saveFormat); + } + + @Override + public String toString() { + return super.toString() + " (from: " + from.format(displayFormat) + " to: " + to.format(displayFormat) + ")"; + } +} diff --git a/src/main/java/task/Task.java b/src/main/java/task/Task.java new file mode 100644 index 000000000..e1ff862af --- /dev/null +++ b/src/main/java/task/Task.java @@ -0,0 +1,75 @@ +package task; + +/** + * Task class that can be marked as done or not done. + */ +public abstract class Task { + protected String description; + protected boolean isDone; + + /** + * Constructs a new Task with description. + * + * @param description The description of the task. + */ + public Task(String description) { + this.description = description; + this.isDone = false; + } + + public Task(String description, boolean isDone) { + this.description = description; + this.isDone = isDone; + } + + /** + * Marks the task as done. + */ + public void markDone() { + this.isDone = true; + } + + /** + * Marks the task as not done. + */ + public void markNotDone() { + this.isDone = false; + } + + /** + * Returns the task's completion status icon. + * + * @return "[X]" if done, "[ ]" otherwise. + */ + public String getIcon() { + return isDone ? "[X]" : "[ ]"; + } + + /** + * Returns the task description. + * + * @return The description of the task. + */ + public String getDescription() { + return description; + } + + /** + * Returns the task type icon. + * + * @return A default task type icon. + */ + public abstract String getTaskType(); + + /** + * Converts the task into a file format. + * + * @return A string representation suitable for file storage. + */ + public abstract String toFileFormat(); + + @Override + public String toString() { + return getTaskType() + getIcon() + " " + getDescription(); + } +} diff --git a/src/main/java/task/Todo.java b/src/main/java/task/Todo.java new file mode 100644 index 000000000..f4929bbbd --- /dev/null +++ b/src/main/java/task/Todo.java @@ -0,0 +1,25 @@ +package task; + +/** + * Represents a To-Do task. + */ +public class Todo extends Task { + + public Todo(String description) { + super(description); + } + + public Todo(String description, boolean isDone) { + super(description, isDone); + } + + @Override + public String getTaskType() { + return "[T]"; + } + + @Override + public String toFileFormat() { + return "T | " + (isDone ? "1" : "0") + " | " + description; + } +} diff --git a/src/main/java/tasklist/TaskList.java b/src/main/java/tasklist/TaskList.java new file mode 100644 index 000000000..0f7d84f57 --- /dev/null +++ b/src/main/java/tasklist/TaskList.java @@ -0,0 +1,130 @@ +package tasklist; + +import task.Task; +import command.EddieException; + +import java.util.ArrayList; +import java.util.List; + +/** + * Manages a list of tasks. + */ +public class TaskList { + private ArrayList tasks; + + /** + * Constructs an empty TaskList. + */ + public TaskList() { + this.tasks = new ArrayList<>(); + } + + /** + * Constructs a TaskList with preloaded tasks. + * + * @param tasks The initial list of tasks. + */ + public TaskList(ArrayList tasks) { + this.tasks = tasks; + } + + /** + * Adds a new task to the list. + * + * @param task The task to add. + */ + public void addTask(Task task) { + tasks.add(task); + } + + /** + * Retrieves a task by its index. + * + * @param index The index of the task. + * @return The task at the given index. + */ + public Task getTask(int index) { + return tasks.get(index); + } + + /** + * Removes a task by its index. + * + * @param index The index of the task to remove. + * @return The removed task. + */ + public Task removeTask(int index) throws EddieException { + if (index < 0 || index >= tasks.size()) { + throw new EddieException("Invalid task number."); + } + return tasks.remove(index); + } + + /** + * Marks or unmarks a task as done. + * + * @param index The index of the task. + * @param isDone True to mark as done, false to unmark. + */ + public void markTask(int index, boolean isDone) throws EddieException { + if (index < 0 || index >= tasks.size()) { + throw new EddieException("Invalid task number."); + } + Task task = tasks.get(index); + if (isDone) { + task.markDone(); + } else { + task.markNotDone(); + } + } + + /** + * Returns the total number of tasks. + * + * @return The size of the task list. + */ + public int size() { + return tasks.size(); + } + + /** + * Returns the last added task. + * + * @return The most recent task. + */ + public Task getLastTask() { + return tasks.get(tasks.size() - 1); + } + + /** + * Returns all tasks as a list. + * + * @return The list of tasks. + */ + public ArrayList getAllTasks() { + return tasks; + } + + /** + * Returns a formatted list of all tasks. + * + * @return A string representation of all tasks. + */ + public String getTaskListString() { + StringBuilder list = new StringBuilder(); + for (int i = 0; i < tasks.size(); i++) { + list.append((i + 1)).append(". ").append(tasks.get(i)).append("\n"); + } + return list.toString(); + } + + public List findTasks(String Keyword){ + List tasks = new ArrayList<>(); + for (Task task : tasks) { + if (task.getDescription().toLowerCase().contains(Keyword.toLowerCase())) { + tasks.add(task); + } + } + return tasks; + } +} diff --git a/src/main/java/ui/ErrorMessages.java b/src/main/java/ui/ErrorMessages.java new file mode 100644 index 000000000..361c1a8a9 --- /dev/null +++ b/src/main/java/ui/ErrorMessages.java @@ -0,0 +1,14 @@ +package ui; + +public class ErrorMessages { + public static final String INVALID_COMMAND = "Oh no! Invalid command. Use: todo, deadline, event, list, mark {num}, or unmark {num}."; + public static final String INVALID_TODO = "Oh no! empty description. Use: todo {task description}"; + public static final String INVALID_DEADLINE = "Oh no! empty description. Use: deadline {task} /by {date}"; + public static final String INVALID_EVENT = "Oh no! empty description. Use: event {task} /from {start} /to {end}"; + public static final String INVALID_TASK_NUMBER = "Oh no! Invalid task number."; + public static final String MKDIR_FAILED = "Oh no! Unable to create directory for saving task."; + public static final String SAVE_TASK_FAILED = "Oh no! Unable to save task: "; + public static final String TASK_LOAD_FAILED = "Oh no! Unable to load task: "; + public static final String TASK_TYPE_UNKNOWN = "Oh no! Unknown task type: "; + public static final String TASK_PARSING_ERROR = "Oh no! parsing task: "; +} diff --git a/src/main/java/ui/Ui.java b/src/main/java/ui/Ui.java new file mode 100644 index 000000000..c7e824b5f --- /dev/null +++ b/src/main/java/ui/Ui.java @@ -0,0 +1,107 @@ +package ui; + +import task.Task; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +public class Ui { + private static final String LOGO = + " _____ ____ ____ ___ _____ \n" + + " | ____|| _ \\| _ \\|_ _|| ____| \n" + + " | _| | | | | | | || | | _| \n" + + " | |___ | |_| | |_| || | | |___ \n" + + " |_____||____/|____/|___||_____| \n"; + + private final Scanner scanner; + + public Ui() { + this.scanner = new Scanner(System.in); + } + + public void showStartPage() { + System.out.println(LOGO); + printLine(); + System.out.println("Eddie: Welcome to your personal task manager!"); + System.out.println("I can help you manage different types of tasks.\n"); + + System.out.println("HOW TO USE EDDIE:"); + System.out.println("To add a ToDo: `todo {task}`"); + System.out.println("To add a Deadline: `deadline {task} /by {date}`"); + System.out.println("To add an Event: `event {task} /from {start} /to {end}`\n"); + + System.out.println("Other Commands:"); + System.out.println("View all tasks: `list`"); + System.out.println("Mark as done: `mark 2`"); + System.out.println("Unmark: `unmark 2`"); + System.out.println("Delete: `delete 2`"); + System.out.println("Find tasks: `find {keyword}`"); + System.out.println("Exit: `bye`"); + printLine(); + } + + public void showEndPage() { + printLine(); + System.out.println("Eddie: Goodbye! Hope to see you again soon!"); + printLine(); + } + + public void showTaskAdded(Task task, int count) { + System.out.println("Eddie:\nGot it. I've added this task:\n " + task); + System.out.println("Now you have " + count + " tasks in the list."); + } + + public void showTaskDeleted(Task task, int count) { + System.out.println("Eddie:\nNoted. I've removed this task:\n " + task); + System.out.println("Now you have " + count + " tasks in the list."); + } + + public void showMarkedStatus(Task task, boolean done) { + if (done) { + System.out.println("Eddie:\nYay! Marked as done:\n " + task + "\nHave a Beer!!"); + } else { + System.out.println("Eddie:\nOh no! Marked as not done:\n " + task); + } + } + + public void showTaskList(ArrayList tasks) { + System.out.println("Eddie:\nHere are the tasks in your list:"); + if (tasks.isEmpty()) { + System.out.println("No tasks in your list."); + return; + } + + for (int i = 0; i < tasks.size(); i++) { + System.out.println((i + 1) + ". " + tasks.get(i)); + } + } + + public void showError(String msg) { + System.out.println("Eddie:\nError: " + msg); + } + + public void printLine() { + System.out.println("_____________________________________________________________________"); + } + + public void showLoadingError() { + System.out.println("Error loading saved tasks. Starting with an empty task list."); + } + + public String readCommand() { + System.out.print("You: "); + return scanner.nextLine().trim(); + } + + public void showMatchingTasks(List matchedTasks) { + System.out.println("Eddie:\nHere are the tasks matching your list:"); + if (matchedTasks.isEmpty()) { + System.out.println("No tasks matching your list."); + } + int index = 1; + for (Task task : matchedTasks) { + System.out.println(index + ". " + task); + index++; + } + } +}