+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index af0309a9e..71de90ad3 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,59 @@
-# Duke project template
+# 🥖 Baguette
-This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it.
+Baguette is a lightweight, command-line task management application designed to help users organize and track their tasks efficiently.
-## Setting up in Intellij
+---
-Prerequisites: JDK 17, update Intellij to the most recent version.
-
-1. Open Intellij (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project first)
-1. Open the project into Intellij as follows:
- 1. Click `Open`.
- 1. Select the project directory, and click `OK`.
- 1. If there are any further prompts, accept the defaults.
-1. Configure the project to use **JDK 17** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk).
- In the same dialog, set the **Project language level** field to the `SDK default` option.
-1. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output:
+### 💻 Setup
+1. Ensure that you have Java 17 installed. Open CMD and type the following:
+ ```
+ java -version
+ ```
+ If another version is detected or if you do not have Java installed, visit:
+ > https://docs.aws.amazon.com/corretto/latest/corretto-17-ug/downloads-list.html
+2. Download `Baguette.v0_3.jar` from the following link and move it to any directory you desire
+ > https://github.com/yangyi-zhu/ip/releases/tag/v0.3
+3. Click the `.jar` file to run. If no window pops up, right-click, copy file path, open CMD and enter the following:
```
- Hello from
- ____ _
- | _ \ _ _| | _____
- | | | | | | | |/ / _ \
- | |_| | |_| | < __/
- |____/ \__,_|_|\_\___|
+ java -jar [path]
```
-**Warning:** Keep the `src\main\java` folder as the root folder for Java files (i.e., don't rename those folders or move Java files to another folder outside of this folder path), as this is the default location some tools (e.g., Gradle) expect to find Java files.
+---
+
+### 📌 Features
+
+### ✅ Task Management
+✍🏻 **Add Tasks:** Create different types of tasks, including:
+- **To-dos:** Simple tasks without a deadline.
+ > **Format:** `todo [description]`
+ > **Example:** `todo Do the laundry`
+- **Deadlines:** Tasks with a specific due date.
+ > **Format:** `deadline [description] ddl: [DD-MM-YY HH:MM]`
+ > **Example:** `deadline CS2113 Increment L5 ddl: 14-02-25 16:00`
+- **Events:** Tasks with a start and end time.
+ > **Format:** `event [description] from: [DD-MM-YY HH:MM] to: [DD-MM-YY HH:MM]`
+ > **Example:** `event Josh's birthday party from: 03-03-25 15:00 to: 04-03-25 00:00`
+
+❌ **Mark & Unmark Tasks:** Keeps track of completed and pending tasks.
+> **Format:** `mark/unmark [index]`
+
+🗑️ **Delete Tasks:** Removes tasks that are no longer needed.
+> **Format:** `delete [index]`
+
+📋 **View Task List:** Displays all stored tasks in an organized format.
+> **Format:** `list`
+
+🔎 **Find Task:** Searches for tasks containing the given string (case-sensitive).
+> **Format:** `find [keyword]`
+
+---
+
+### ❓ FAQs
+**Q:** How do I transfer data to a friend or another device of my own?
+**A:** Locate the directory where you stored the `.jar` file. Open `../data` and your list should be saved in a file called `tasks.txt`. Place this file in the `../data` directory on the secondary device and run the program as described above.
+
+---
+
+### 🛠️ Known Issues
+By default, completed tasks are marked with a checkmark `✓` symbol. This is not supported by all devices, and the user might see a question mark `?` in its place.
+> If this is the case, the user can fix this by forking and cloning this repository to their local device, and replacing the checkmark with another character at Line 67 in `src/main/java/baguette/datatypes/Task.java`.
diff --git a/data/tasks.txt b/data/tasks.txt
new file mode 100644
index 000000000..3bf8c4a0f
--- /dev/null
+++ b/data/tasks.txt
@@ -0,0 +1,2 @@
+D N eaijohdfa *$#24 Oct 2025 12:03
+E Y 12uasdhidusha :=&30 Mar 2025 16:00 +-+01 Apr 2025 02:00
diff --git a/docs/README.md b/docs/README.md
index 47b9f984f..71de90ad3 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,30 +1,59 @@
-# Duke User Guide
-
-// Update the title above to match the actual product name
-
-// Product screenshot goes here
-
-// Product intro goes here
-
-## Adding deadlines
-
-// Describe the action and its outcome.
-
-// Give examples of usage
-
-Example: `keyword (optional arguments)`
-
-// A description of the expected outcome goes here
-
-```
-expected output
-```
-
-## Feature ABC
-
-// Feature details
-
-
-## Feature XYZ
-
-// Feature details
\ No newline at end of file
+# 🥖 Baguette
+
+Baguette is a lightweight, command-line task management application designed to help users organize and track their tasks efficiently.
+
+---
+
+### 💻 Setup
+1. Ensure that you have Java 17 installed. Open CMD and type the following:
+ ```
+ java -version
+ ```
+ If another version is detected or if you do not have Java installed, visit:
+ > https://docs.aws.amazon.com/corretto/latest/corretto-17-ug/downloads-list.html
+2. Download `Baguette.v0_3.jar` from the following link and move it to any directory you desire
+ > https://github.com/yangyi-zhu/ip/releases/tag/v0.3
+3. Click the `.jar` file to run. If no window pops up, right-click, copy file path, open CMD and enter the following:
+ ```
+ java -jar [path]
+ ```
+
+---
+
+### 📌 Features
+
+### ✅ Task Management
+✍🏻 **Add Tasks:** Create different types of tasks, including:
+- **To-dos:** Simple tasks without a deadline.
+ > **Format:** `todo [description]`
+ > **Example:** `todo Do the laundry`
+- **Deadlines:** Tasks with a specific due date.
+ > **Format:** `deadline [description] ddl: [DD-MM-YY HH:MM]`
+ > **Example:** `deadline CS2113 Increment L5 ddl: 14-02-25 16:00`
+- **Events:** Tasks with a start and end time.
+ > **Format:** `event [description] from: [DD-MM-YY HH:MM] to: [DD-MM-YY HH:MM]`
+ > **Example:** `event Josh's birthday party from: 03-03-25 15:00 to: 04-03-25 00:00`
+
+❌ **Mark & Unmark Tasks:** Keeps track of completed and pending tasks.
+> **Format:** `mark/unmark [index]`
+
+🗑️ **Delete Tasks:** Removes tasks that are no longer needed.
+> **Format:** `delete [index]`
+
+📋 **View Task List:** Displays all stored tasks in an organized format.
+> **Format:** `list`
+
+🔎 **Find Task:** Searches for tasks containing the given string (case-sensitive).
+> **Format:** `find [keyword]`
+
+---
+
+### ❓ FAQs
+**Q:** How do I transfer data to a friend or another device of my own?
+**A:** Locate the directory where you stored the `.jar` file. Open `../data` and your list should be saved in a file called `tasks.txt`. Place this file in the `../data` directory on the secondary device and run the program as described above.
+
+---
+
+### 🛠️ Known Issues
+By default, completed tasks are marked with a checkmark `✓` symbol. This is not supported by all devices, and the user might see a question mark `?` in its place.
+> If this is the case, the user can fix this by forking and cloning this repository to their local device, and replacing the checkmark with another character at Line 67 in `src/main/java/baguette/datatypes/Task.java`.
diff --git a/out/artifacts/ip_jar/ip.jar b/out/artifacts/ip_jar/ip.jar
new file mode 100644
index 000000000..422682582
Binary files /dev/null and b/out/artifacts/ip_jar/ip.jar differ
diff --git a/out/artifacts/ip_jar2/ip.jar b/out/artifacts/ip_jar2/ip.jar
new file mode 100644
index 000000000..029f54814
Binary files /dev/null and b/out/artifacts/ip_jar2/ip.jar differ
diff --git a/out/production/ip/META-INF/MANIFEST.MF b/out/production/ip/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..109596c1a
--- /dev/null
+++ b/out/production/ip/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: baguette.Baguette
+
diff --git a/out/production/ip/baguette/Baguette.class b/out/production/ip/baguette/Baguette.class
new file mode 100644
index 000000000..e3c87922b
Binary files /dev/null and b/out/production/ip/baguette/Baguette.class differ
diff --git a/out/production/ip/baguette/BaguetteException.class b/out/production/ip/baguette/BaguetteException.class
new file mode 100644
index 000000000..a70ec5030
Binary files /dev/null and b/out/production/ip/baguette/BaguetteException.class differ
diff --git a/out/production/ip/baguette/Constants.class b/out/production/ip/baguette/Constants.class
new file mode 100644
index 000000000..405f38b90
Binary files /dev/null and b/out/production/ip/baguette/Constants.class differ
diff --git a/out/production/ip/baguette/Parser.class b/out/production/ip/baguette/Parser.class
new file mode 100644
index 000000000..ef146457c
Binary files /dev/null and b/out/production/ip/baguette/Parser.class differ
diff --git a/out/production/ip/baguette/Storage.class b/out/production/ip/baguette/Storage.class
new file mode 100644
index 000000000..7fe4d57ec
Binary files /dev/null and b/out/production/ip/baguette/Storage.class differ
diff --git a/out/production/ip/baguette/TaskList.class b/out/production/ip/baguette/TaskList.class
new file mode 100644
index 000000000..ffe4a0052
Binary files /dev/null and b/out/production/ip/baguette/TaskList.class differ
diff --git a/out/production/ip/baguette/Ui.class b/out/production/ip/baguette/Ui.class
new file mode 100644
index 000000000..29751e3fc
Binary files /dev/null and b/out/production/ip/baguette/Ui.class differ
diff --git a/out/production/ip/baguette/datatypes/Deadline.class b/out/production/ip/baguette/datatypes/Deadline.class
new file mode 100644
index 000000000..c78d73776
Binary files /dev/null and b/out/production/ip/baguette/datatypes/Deadline.class differ
diff --git a/out/production/ip/baguette/datatypes/Event.class b/out/production/ip/baguette/datatypes/Event.class
new file mode 100644
index 000000000..2c109b380
Binary files /dev/null and b/out/production/ip/baguette/datatypes/Event.class differ
diff --git a/out/production/ip/baguette/datatypes/Task.class b/out/production/ip/baguette/datatypes/Task.class
new file mode 100644
index 000000000..423a2e4f9
Binary files /dev/null and b/out/production/ip/baguette/datatypes/Task.class differ
diff --git a/out/production/ip/baguette/datatypes/Todo.class b/out/production/ip/baguette/datatypes/Todo.class
new file mode 100644
index 000000000..b740e0866
Binary files /dev/null and b/out/production/ip/baguette/datatypes/Todo.class differ
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/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..109596c1a
--- /dev/null
+++ b/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: baguette.Baguette
+
diff --git a/src/main/java/baguette/Baguette.java b/src/main/java/baguette/Baguette.java
new file mode 100644
index 000000000..5b19df126
--- /dev/null
+++ b/src/main/java/baguette/Baguette.java
@@ -0,0 +1,53 @@
+package baguette;
+
+import java.util.Scanner;
+
+/**
+ * The main class of the Baguette application.
+ */
+public class Baguette {
+ private static TaskList list;
+
+ /**
+ * Initializes a new Baguette instance by creating a TaskList and loading saved tasks.
+ * If an error occurs while loading, an error message is displayed.
+ */
+ public Baguette() {
+ list = new TaskList();
+ try {
+ list.loadList();
+ } catch (Exception e) {
+ Ui.printLoadingError();
+ }
+ }
+
+ /**
+ * Runs the main program loop, handling user input and executing commands.
+ * Prints the logo and welcome message before entering the input loop.
+ * Continues to process input until the user enters "bye".
+ */
+ public static void run() {
+ Scanner scanner = new Scanner(System.in);
+
+ Ui.printLogo();
+ Ui.printWelcomeMessage();
+
+ while (true) {
+ String input = scanner.nextLine();
+ if (input.equals("bye")) {
+ Ui.printGoodbyeMessage();
+ return;
+ }
+ Parser.checkInput(input);
+ }
+ }
+
+ /**
+ * The main method. Creates a Baguette instance and starts the program.
+ *
+ * @param args Command-line arguments.
+ */
+ public static void main(String[] args) {
+ new Baguette().run();
+ }
+}
diff --git a/src/main/java/baguette/BaguetteException.java b/src/main/java/baguette/BaguetteException.java
new file mode 100644
index 000000000..99ad579c9
--- /dev/null
+++ b/src/main/java/baguette/BaguetteException.java
@@ -0,0 +1,15 @@
+package baguette;
+
+/**
+ * Custom exception class for handling errors specific to the Baguette application.
+ */
+public class BaguetteException extends Exception {
+ /**
+ * Constructs a new BaguetteException with the specified message.
+ *
+ * @param message The message to be displayed to the user.
+ */
+ public BaguetteException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/baguette/Constants.java b/src/main/java/baguette/Constants.java
new file mode 100644
index 000000000..6333d1987
--- /dev/null
+++ b/src/main/java/baguette/Constants.java
@@ -0,0 +1,74 @@
+package baguette;
+
+/**
+ * Stores constants, primarily strings, used in the Baguette application.
+ */
+public final class Constants {
+ public static final String DIVIDER = "--------------------------";
+ public static final String NEW_LINE = System.lineSeparator();
+
+ public static final String baguetteLogo = Constants.DIVIDER + Constants.DIVIDER + Constants.DIVIDER + Constants.NEW_LINE
+ + " _______ _______ _______ __ __ _______ _______ _______ _______" + Constants.NEW_LINE
+ + " | _ || _ || || | | || || || || |" + Constants.NEW_LINE
+ + " | |_| || |_| || ___|| | | || ___||_ _||_ _|| ___|" + Constants.NEW_LINE
+ + " | || || | __ | |_| || |___ | | | | | |___" + Constants.NEW_LINE
+ + " | _ | | || || || || ___| | | | | | ___|" + Constants.NEW_LINE
+ + " | |_| || _ || |_| || || |___ | | | | | |___" + Constants.NEW_LINE
+ + " |_______||__| |__||_______||_______||_______| |___| |___| |_______|" + Constants.NEW_LINE
+ + Constants.DIVIDER + Constants.DIVIDER + Constants.DIVIDER + Constants.NEW_LINE;
+
+ public static final String STATUS_MARK = "Congratulations! The following task has been marked as complete:";
+ public static final String STATUS_UNMARK = "Got it! The following task has been marked as incomplete:";
+ public static final String STATUS_DELETE = "Got it! The following task has been deleted:";
+ public static final String STATUS_EMPTY_RESULTS = "Looks like we weren't able to find anything, try something else?";
+ public static final String STATUS_FOUND = " found! Here you go:";
+
+ public static final String WARN_BLANK_DESCRIPTION = "You may forget things if the description was left blank! Try adding a message!";
+ public static final String WARN_ADD_FAILED = NEW_LINE + DIVIDER + NEW_LINE
+ + "The supported types and corresponding formats are as follows:" + NEW_LINE
+ + "To-Dos: todo [description]" + NEW_LINE
+ + "Deadlines: deadline [description] ddl: [DD-MM-YY HH:MM]" + NEW_LINE
+ + "Events: event [description] from: [DD-MM-YY HH:MM] to: [DD-MM-YY HH:MM]" + NEW_LINE
+ + NEW_LINE + "Examples:" + NEW_LINE
+ + "todo Do the laundry" + NEW_LINE
+ + "deadline CS2113 Increment L5 ddl: 14-02-25 16:00" + NEW_LINE
+ + "event Josh's birthday party from: 03-03-25 15:00 to: 04-03-25 00:00"
+ + NEW_LINE + DIVIDER + NEW_LINE
+ + "Other commands: list | mark [index] | unmark [index] | delete [index] | find [keyword] | bye"
+ + NEW_LINE + DIVIDER + NEW_LINE;;
+ public static final String WARN_DELETE_FAILED = NEW_LINE + DIVIDER + NEW_LINE
+ + "Failed to delete task, try again with a valid index within your task count!"
+ + NEW_LINE + "Run \"list\" to see your current list of tasks."
+ + NEW_LINE + DIVIDER + NEW_LINE;
+ public static final String WARN_MISSING_DEADLINE = "Hon hon hon, looks like you forgot what this type is for";
+ public static final String WARN_MISSING_EVENT = "Hola amigo! Make sure you follow the correct format!";
+ public static final String WARN_FIELD_EMPTY = "Make sure you fill all fields!!! :P";
+ public static final String WARN_INT = "OI OI OI! Integer, my friend!";
+ public static final String WARN_LOADING_ERROR = "Oh no, we ran into an unexpected error!"
+ + NEW_LINE + "Try relaunching the program!";
+ public static final String WARN_INVALID_DATE_FORMAT = "Invalid date format! Expected format: dd-MM-yy HH:mm"
+ + NEW_LINE + "Defaulting to current time...";
+ public static final String WARN_COMPLETE = "Task already completed! Try doing something else!";
+ public static final String WARN_INCOMPLETE = "Cannot unmark an incomplete task! Try again!";
+
+ public static final String WELCOME = DIVIDER + NEW_LINE
+ + "Bonjour et bievenue! Vous pouvez m'appeler Baguette!"
+ + NEW_LINE + "What do you think about my intro? :D" + NEW_LINE
+ + "Anyhow, what are we thinking today?"
+ + NEW_LINE + DIVIDER + NEW_LINE;
+ public static final String GOODBYE = NEW_LINE + DIVIDER + NEW_LINE
+ + "Have a nice day!" + NEW_LINE + DIVIDER;
+
+ public static int INDEX_TODO = 5;
+ public static int INDEX_DEADLINE = 9;
+ public static int INDEX_EVENT = 6;
+
+ public static int STORAGE_TIME_WIDTH = 3;
+ public static int STORAGE_PREFIX_WIDTH = 3;
+
+ public static String PREFIX_D = "*$#";
+ public static String PREFIX_E_F = ":=&";
+ public static String PREFIX_E_T = "+-+";
+
+ private Constants() {}
+}
\ No newline at end of file
diff --git a/src/main/java/baguette/Parser.java b/src/main/java/baguette/Parser.java
new file mode 100644
index 000000000..0b9360bf7
--- /dev/null
+++ b/src/main/java/baguette/Parser.java
@@ -0,0 +1,92 @@
+package baguette;
+import java.time.LocalDateTime;
+import java.util.Locale;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+
+/**
+ * Handles parsing of user input and date-time formatting.
+ */
+public class Parser {
+ /**
+ * Handles errors related to invalid indexing.
+ * Delegates command parsing to {@code handleCommand(String)}
+ *
+ * @param message The user input command to be processed.
+ */
+ public static void checkInput(String message) {
+ try {
+ handleCommand(message);
+ } catch (NumberFormatException e) {
+ Ui.printIntegerError();
+ }
+ }
+
+ /**
+ * Processes the user input and executes commands based on prefix.
+ *
+ * @param message The user input command to be processed.
+ * @throws NumberFormatException If an invalid format is detected for indexing.
+ */
+ public static void handleCommand(String message) throws NumberFormatException {
+ if (message.startsWith("list")) {
+ Ui.printList();
+ } else if (message.startsWith("delete ")) {
+ int index = Integer.parseInt(message.substring(7));
+ TaskList.deleteTask(index - 1);
+ } else if (message.startsWith("mark ")) {
+ int index = Integer.parseInt(message.substring(5));
+ TaskList.toggleMarkTask(index - 1, true);
+ } else if (message.startsWith("unmark ")) {
+ int index = Integer.parseInt(message.substring(7));
+ TaskList.toggleMarkTask(index - 1, false);
+ } else if (message.startsWith("find ")) {
+ TaskList.find(message.substring(5).trim());
+ } else {
+ TaskList.addTask(message);
+ }
+ }
+
+ /**
+ * Extracts the task type from the given message.
+ *
+ * @param message The user command.
+ * @return The task type as a string.
+ */
+ public static String parseTaskType(String message) {
+ message = message.trim();
+ int index = message.indexOf(" ");
+ return (index == -1) ? "" : message.substring(0, index);
+ }
+
+ /**
+ * Parses a date-time string into a LocalDateTime object.
+ * Defaults to current time upon encountering invalid date formats.
+ *
+ * @param message The date-time string in "yyyy-MM-dd HH:mm" format.
+ * @return The parsed LocalDateTime object.
+ */
+ public static LocalDateTime parseDateTime(String message) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yy HH:mm", Locale.ENGLISH);
+
+ try {
+ return LocalDateTime.parse(message, formatter);
+ } catch (DateTimeParseException e) {
+ System.out.println(new BaguetteException(Constants.WARN_INVALID_DATE_FORMAT));
+ return LocalDateTime.now();
+ }
+ }
+
+ /**
+ * Converts a LocalDateTime object into a formatted string representation.
+ *
+ * @param dateTime The LocalDateTime object to be formatted.
+ * @return The formatted date-time string in "dd MMM yyyy HH:mm" format.
+ */
+ public static String toStringDateTime(LocalDateTime dateTime) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm", Locale.ENGLISH);
+ return dateTime.format(formatter);
+ }
+
+ private Parser() {}
+}
diff --git a/src/main/java/baguette/Storage.java b/src/main/java/baguette/Storage.java
new file mode 100644
index 000000000..fac282518
--- /dev/null
+++ b/src/main/java/baguette/Storage.java
@@ -0,0 +1,158 @@
+package baguette;
+
+import baguette.datatypes.Task;
+import baguette.datatypes.Todo;
+import baguette.datatypes.Deadline;
+import baguette.datatypes.Event;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.time.format.DateTimeFormatter;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.Scanner;
+
+/**
+ * Handles file storage operations for saving and loading tasks locally.
+ */
+public class Storage {
+ private static final String folderPath = "data";
+ private static final String filePath = "./data/tasks.txt";
+
+ /**
+ * Parses a task from a line of text retrieved from the stored file.
+ * Determines the task type (Todo, Deadline, or Event) and reconstructs the task.
+ *
+ * @param line A line of text from the stored file representing a task.
+ * @return A Task object parsed from the given line.
+ */
+ public static Task parseTask(String line) {
+ Task task;
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm", Locale.ENGLISH);
+ switch (line.substring(0, 1)) {
+ case "T":
+ task = new Todo(line.substring(Constants.STORAGE_PREFIX_WIDTH).trim());
+ break;
+ case "D":
+ int ddlIndex = line.indexOf(Constants.PREFIX_D);
+ task = new Deadline(line.substring(Constants.STORAGE_PREFIX_WIDTH, ddlIndex).trim(),
+ LocalDateTime.parse(line.substring(ddlIndex + Constants.STORAGE_TIME_WIDTH).trim(), formatter));
+ break;
+ case "E":
+ int fromIndex = line.indexOf(Constants.PREFIX_E_F);
+ int toIndex = line.indexOf(Constants.PREFIX_E_T);
+ System.out.println(line.substring(fromIndex + Constants.STORAGE_TIME_WIDTH, toIndex));
+ task = new Event(line.substring(Constants.STORAGE_PREFIX_WIDTH, fromIndex).trim(),
+ LocalDateTime.parse(line.substring(fromIndex + Constants.STORAGE_TIME_WIDTH, toIndex).trim(), formatter),
+ LocalDateTime.parse(line.substring(toIndex + Constants.STORAGE_TIME_WIDTH).trim(), formatter));
+ break;
+ default:
+ task = new Todo(line);
+ break;
+ }
+ if (line.substring(2,3).equals("Y")) {
+ task.mark();
+ }
+ return task;
+ }
+
+ /**
+ * Loads tasks by reading from the storage file into memory.
+ * Ensures target directory and file exist before reading.
+ * Parses non-empty lines into Tasks.
+ *
+ * @return An {@code ArrayList} containing the tasks retrieved from the stored file.
+ * @throws RuntimeException If an I/O error occurs while reading.
+ */
+ public static ArrayList generateList() {
+ ensureStorage();
+
+ ArrayList list = new ArrayList<>();
+ File file = new File(filePath);
+
+ try (Scanner scanner = new Scanner(file)) {
+ populateTaskList(scanner, list);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ return list;
+ }
+
+ /**
+ * Reads lines from the scanner and adds valid tasks to the list.
+ *
+ * @param scanner The Scanner to read from.
+ * @param list The list to populate with parsed tasks.
+ */
+ public static void populateTaskList(Scanner scanner, ArrayList list) {
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine().trim();
+ if (!line.isEmpty()) {
+ list.add(parseTask(line));
+ }
+ }
+ }
+
+ /**
+ * Ensures that the target storage folder and task files exist.
+ * Creates directory and file if missing.
+ * Throws runtime exception if an I/O error occurs.
+ */
+ public static void ensureStorage() {
+ File folder = new File(folderPath);
+ File file = new File(filePath);
+
+ try {
+ if (!folder.exists()) {
+ folder.mkdirs();
+ }
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Updates the stored file with the current list of tasks.
+ * Overwrites the existing file with the updated task list.
+ *
+ * @param tasks The list of tasks to be saved in the storage file.
+ * @throws IOException If an error occurs while writing to the file.
+ */
+ public static void updateFile(ArrayList tasks) throws IOException {
+ FileWriter fw = new FileWriter(filePath);
+ for (Task task : tasks) {
+ fw.write(convertTaskToFile(task) + Constants.NEW_LINE);
+ }
+ fw.close();
+ }
+
+ /**
+ * Converts a Task object into a string format suitable for storage in a file.
+ * Formats Todo, Deadline, and Event tasks accordingly.
+ *
+ * @param task The Task object to be converted.
+ * @return A formatted string representation of the task for file storage.
+ */
+ public static String convertTaskToFile(Task task) {
+ switch (task.getType()) {
+ case "[T]":
+ return "T " + ((task.getIsDone()) ? "Y " : "N ") + task.getDesc();
+ case "[D]":
+ Deadline deadline = (Deadline) task;
+ return "D " + ((task.getIsDone()) ? "Y " : "N ") + task.getDesc() + " " +
+ Constants.PREFIX_D + deadline.getDdl();
+ case "[E]":
+ Event event = (Event) task;
+ return "E " + ((task.getIsDone()) ? "Y " : "N ") + task.getDesc() + " " +
+ Constants.PREFIX_E_F + event.getFrom() + " " +
+ Constants.PREFIX_E_T+ event.getTo();
+ }
+ return "";
+ }
+}
diff --git a/src/main/java/baguette/TaskList.java b/src/main/java/baguette/TaskList.java
new file mode 100644
index 000000000..31005bae3
--- /dev/null
+++ b/src/main/java/baguette/TaskList.java
@@ -0,0 +1,232 @@
+package baguette;
+
+import baguette.datatypes.Deadline;
+import baguette.datatypes.Event;
+import baguette.datatypes.Task;
+import baguette.datatypes.Todo;
+
+import java.util.ArrayList;
+
+/**
+ * Manages the list of tasks in the Baguette application.
+ * Provides methods for adding, deleting, marking, unmarking, and searching tasks.
+ */
+public class TaskList {
+ private static ArrayList tasks;
+
+ /**
+ * Constructs a new TaskList instance with an empty task list.
+ */
+ public TaskList() {
+ tasks = new ArrayList();
+ }
+
+ /**
+ * Retrieves the list of tasks.
+ *
+ * @return The current list of tasks.
+ */
+ public static ArrayList getList() {
+ return tasks;
+ }
+
+ /**
+ * Deletes a task from the task list at the specified index.
+ * Displays a success or failure message based on the validity of the index.
+ *
+ * @param index The index of the task to be deleted.
+ */
+ public static void deleteTask(int index) {
+ if (index < 0 || index >= tasks.size()) {
+ Ui.printDeleteFail();
+ return;
+ }
+
+ Ui.printDeleteSuccess(tasks.get(index).toString());
+ tasks.remove(index);
+
+ try {
+ Storage.updateFile(tasks);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Marks or unmarks a task at the specified index.
+ * Displays the updated task status.
+ *
+ * @param index The index of the task to be marked or unmarked.
+ * @param isMarked True if marking the task as completed, false if unmarking.
+ */
+ public static void toggleMarkTask(int index, boolean isMarked) {
+ if (index < 0 || index >= tasks.size()) {
+ Ui.printToggleMarkFail(isMarked);
+ return;
+ }
+
+ Task task = tasks.get(index);
+ if (isMarked && !task.getIsDone()) {
+ task.mark();
+ Ui.printMarkStatus(tasks.get(index), isMarked);
+ } else if (!isMarked && task.getIsDone()) {
+ task.unmark();
+ Ui.printMarkStatus(tasks.get(index), isMarked);
+ } else if (isMarked && task.getIsDone()) {
+ Ui.printAlreadyCompleted();
+ } else {
+ Ui.printIncomplete();
+ }
+
+ try {
+ Storage.updateFile(tasks);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Adds a new Todo task to the task list.
+ *
+ * @param message The user input containing the task description.
+ * @return True if the task was successfully added, false otherwise.
+ */
+ public static boolean addTodo(String message) {
+ int index = Constants.INDEX_TODO;
+ try {
+ if (message.substring(index).isBlank()) {
+ throw new BaguetteException(Constants.WARN_BLANK_DESCRIPTION);
+ }
+ tasks.add(new Todo(message.substring(index).trim()));
+ return true;
+ } catch (BaguetteException e) {
+ System.out.println(e);
+ return false;
+ }
+ }
+
+ /**
+ * Adds a new Deadline task to the task list.
+ *
+ * @param message The user input containing the task description and deadline.
+ * @return True if the task was successfully added, false otherwise.
+ */
+ public static boolean addDeadline(String message) {
+ int index = Constants.INDEX_DEADLINE;
+ int indexDdl = message.indexOf("ddl: ");
+
+ try {
+ if (indexDdl == -1) {
+ throw new BaguetteException(Constants.WARN_MISSING_DEADLINE);
+ }
+
+ String description = message.substring(index, indexDdl).trim();
+ String deadline = message.substring(indexDdl + 5).trim();
+
+ if (description.isEmpty() || deadline.isEmpty()) {
+ throw new BaguetteException(Constants.WARN_FIELD_EMPTY);
+ }
+
+ tasks.add(new Deadline(description, deadline));
+ return true;
+ } catch (BaguetteException e) {
+ System.out.println(e);
+ return false;
+ }
+ }
+
+ /**
+ * Adds a new Event task to the task list.
+ *
+ * @param message The user input containing the task description, start, and end time.
+ * @return True if the task was successfully added, false otherwise.
+ */
+ public static boolean addEvent(String message) {
+ int index = Constants.INDEX_EVENT;
+ int indexFrom = message.indexOf("from: ");
+ int indexTo = message.indexOf("to: ");
+
+ try {
+ if (indexFrom == -1 || indexTo == -1 || indexFrom >= indexTo) {
+ throw new BaguetteException(Constants.WARN_MISSING_EVENT);
+ }
+
+ String description = message.substring(index, indexFrom).trim();
+ String from = message.substring(indexFrom + 6, indexTo).trim();
+ String to = message.substring(indexTo + 4).trim();
+
+ if (description.isEmpty() || from.isEmpty() || to.isEmpty()) {
+ throw new BaguetteException(Constants.WARN_FIELD_EMPTY);
+ }
+
+ tasks.add(new Event(description, from, to));
+ return true;
+ } catch (BaguetteException e) {
+ System.out.println(e);
+ return false;
+ }
+ }
+
+ /**
+ * Determines the type of task from user input and adds it to the task list.
+ * Supports "todo", "deadline", and "event" task types.
+ * Prints status message.
+ * Updates the file locally if successful.
+ *
+ * @param message The user input containing the task details.
+ */
+ public static void addTask(String message) {
+ boolean isAddSuccess;
+
+ switch (Parser.parseTaskType(message)) {
+ case "todo":
+ isAddSuccess = addTodo(message);
+ break;
+ case "deadline":
+ isAddSuccess = addDeadline(message);
+ break;
+ case "event":
+ isAddSuccess = addEvent(message);
+ break;
+ default:
+ Ui.printAddFail();
+ return;
+ }
+
+ if (isAddSuccess) {
+ try {
+ Storage.updateFile(tasks);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ Ui.printAddSuccess(tasks.get(tasks.size() - 1), tasks.size());
+ }
+ }
+
+ /**
+ * Loads tasks from the storage file and adds them to the task list.
+ */
+ public static void loadList() {
+ ArrayList fileContent = Storage.generateList();
+ tasks.addAll(fileContent);
+ }
+
+ /**
+ * Searches for tasks in the list that contain the given keyword.
+ * Displays the matching tasks.
+ *
+ * @param keyword The search term to filter tasks.
+ */
+ public static void find(String keyword) {
+ ArrayList list = new ArrayList();
+
+ for (Task task : tasks) {
+ if (task.getDesc().indexOf(keyword) != -1) {
+ list.add(task);
+ }
+ }
+
+ Ui.printSearchResults(list);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/baguette/Ui.java b/src/main/java/baguette/Ui.java
new file mode 100644
index 000000000..a9a3c3774
--- /dev/null
+++ b/src/main/java/baguette/Ui.java
@@ -0,0 +1,154 @@
+package baguette;
+
+import baguette.datatypes.Task;
+
+import java.util.ArrayList;
+
+/**
+ * Handles user interface-related operations such as displaying messages and task lists.
+ */
+public class Ui {
+ /**
+ * Prints an error message when loading tasks from storage fails.
+ */
+ public static void printLoadingError() {
+ System.out.println(Constants.WARN_LOADING_ERROR);
+ }
+
+ /**
+ * Prints the application logo.
+ */
+ public static void printLogo() {
+ System.out.println(Constants.baguetteLogo);
+ }
+
+ /**
+ * Prints the welcome message when the application starts.
+ */
+ public static void printWelcomeMessage() {
+ System.out.println(Constants.WELCOME);
+ }
+
+ /**
+ * Prints the goodbye message when the application exits.
+ */
+ public static void printGoodbyeMessage() {
+ System.out.println(Constants.GOODBYE);
+ }
+
+ /**
+ * Prints a success message when a task is successfully deleted.
+ *
+ * @param task The task that was deleted.
+ */
+ public static void printDeleteSuccess(String task) {
+ System.out.println(Constants.NEW_LINE + Constants.DIVIDER + Constants.NEW_LINE
+ + Constants.STATUS_DELETE + Constants.NEW_LINE + " " + task
+ + Constants.NEW_LINE + Constants.DIVIDER + Constants.NEW_LINE);
+ }
+
+ /**
+ * Prints an error message when a task deletion attempt fails.
+ */
+ public static void printDeleteFail() {
+ System.out.println(Constants.WARN_DELETE_FAILED);
+ }
+
+ /**
+ * Prints the updated status of a task after being marked or unmarked.
+ *
+ * @param task The task whose status was updated.
+ * @param isMarked True if the task was marked as completed, false if unmarked.
+ */
+ public static void printMarkStatus(Task task, boolean isMarked) {
+ System.out.println(Constants.NEW_LINE + Constants.DIVIDER + Constants.NEW_LINE
+ + (isMarked ? Constants.STATUS_MARK : Constants.STATUS_UNMARK)
+ + Constants.NEW_LINE + " " + task + Constants.NEW_LINE
+ + Constants.DIVIDER + Constants.NEW_LINE);
+ }
+
+ /**
+ * Prints a success message when a task is successfully added.
+ *
+ * @param task The task that was added.
+ * @param size The total number of tasks after adding the new task.
+ */
+ public static void printAddSuccess(Task task, int size) {
+ System.out.println(Constants.NEW_LINE + Constants.DIVIDER + Constants.NEW_LINE
+ + "The following task has been added:"
+ + Constants.NEW_LINE + " " + task + Constants.NEW_LINE
+ + "You now have " + size + " tasks in the tasks."
+ + Constants.NEW_LINE + Constants.DIVIDER + Constants.NEW_LINE);
+ }
+
+ /**
+ * Prints an error message when a task addition attempt fails.
+ */
+ public static void printAddFail() {
+ System.out.println(Constants.WARN_ADD_FAILED);
+ }
+
+ /**
+ * Prints the list of tasks currently stored.
+ */
+ public static void printList() {
+ ArrayList tasks = TaskList.getList();
+ System.out.println(Constants.NEW_LINE + Constants.DIVIDER + Constants.NEW_LINE + "Tasks:");
+ for (int i = 0; i < tasks.size(); i++) {
+ System.out.println((i + 1) + ". " + tasks.get(i));
+ }
+ System.out.println(Constants.DIVIDER + Constants.NEW_LINE);
+ }
+
+ /**
+ * Prints the search results based on a keyword query.
+ *
+ * @param list The list of tasks that match the search criteria.
+ */
+ public static void printSearchResults(ArrayList list) {
+ System.out.println(Constants.NEW_LINE + Constants.DIVIDER);
+ if (list.size() == 0) {
+ System.out.println(Constants.STATUS_EMPTY_RESULTS);
+ } else {
+ System.out.println(list.size() + (list.size() == 1 ? " result" : " results")
+ + Constants.STATUS_FOUND + Constants.NEW_LINE);
+ for (int i = 0; i < list.size(); i++) {
+ System.out.println((i + 1) + ". " + list.get(i));
+ }
+ }
+ System.out.println(Constants.DIVIDER + Constants.NEW_LINE);
+ }
+
+ /**
+ * Prints an error message when marking/unmarking fails due to invalid indexing.
+ *
+ * @param isMark Whether the task was to be marked as complete or otherwise.
+ */
+ public static void printToggleMarkFail(boolean isMark) {
+ String output = Constants.NEW_LINE + Constants.DIVIDER + Constants.NEW_LINE
+ + "Failed to " + (isMark ? "mark" : "unmark") + " as completed, "
+ + "index out of bounds."
+ + Constants.NEW_LINE + Constants.DIVIDER + Constants.NEW_LINE;
+ System.out.println(output);
+ }
+
+ /**
+ * Prints an error message when an Integer is expected but another format is detected.
+ */
+ public static void printIntegerError() {
+ System.out.println(Constants.NEW_LINE + Constants.DIVIDER + Constants.NEW_LINE
+ + Constants.WARN_INT + Constants.NEW_LINE + Constants.DIVIDER + Constants.NEW_LINE);
+ }
+
+ public static void printAlreadyCompleted() {
+ System.out.println(Constants.NEW_LINE + Constants.DIVIDER + Constants.NEW_LINE
+ + Constants.WARN_COMPLETE + Constants.NEW_LINE + Constants.DIVIDER + Constants.NEW_LINE);
+ }
+
+ public static void printIncomplete() {
+ System.out.println(Constants.NEW_LINE + Constants.DIVIDER + Constants.NEW_LINE
+ + Constants.WARN_INCOMPLETE + Constants.NEW_LINE + Constants.DIVIDER + Constants.NEW_LINE);
+ }
+
+ private Ui() {}
+}
diff --git a/src/main/java/baguette/datatypes/Deadline.java b/src/main/java/baguette/datatypes/Deadline.java
new file mode 100644
index 000000000..124ca4c71
--- /dev/null
+++ b/src/main/java/baguette/datatypes/Deadline.java
@@ -0,0 +1,62 @@
+package baguette.datatypes;
+
+import baguette.Parser;
+import java.time.LocalDateTime;
+
+/**
+ * Represents a task with a deadline in the Baguette application.
+ * Inherits from the Task class and includes a due date.
+ */
+public class Deadline extends Task {
+ protected LocalDateTime ddl;
+
+ /**
+ * Constructs a new Deadline task with the specified description and deadline.
+ *
+ * @param description The description of the task.
+ * @param deadline The due date and time in string format, which is parsed into a LocalDateTime object.
+ */
+ public Deadline(String description, String deadline) {
+ super(description);
+ this.ddl = Parser.parseDateTime(deadline);
+ }
+
+ /**
+ * Constructs a new Deadline task with the specified description and deadline.
+ *
+ * @param description The description of the task.
+ * @param deadline The due date and time in LocalDateTime format, which is parsed into a LocalDateTime object.
+ */
+ public Deadline(String description, LocalDateTime deadline) {
+ super(description);
+ this.ddl = deadline;
+ }
+
+ /**
+ * Retrieves the type identifier for the deadline task.
+ *
+ * @return A string representing the task type, "[D]" for deadline.
+ */
+ public String getType() {
+ return "[D]";
+ }
+
+ /**
+ * Retrieves the formatted deadline date and time as a string.
+ *
+ * @return A string representation of the deadline.
+ */
+ public String getDdl() {
+ return Parser.toStringDateTime(ddl);
+ }
+
+ /**
+ * Returns a string representation of the deadline task, including its description and due date.
+ *
+ * @return A formatted string representation of the task.
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " (due by: " + getDdl() + ")";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/baguette/datatypes/Event.java b/src/main/java/baguette/datatypes/Event.java
new file mode 100644
index 000000000..4cc5004a0
--- /dev/null
+++ b/src/main/java/baguette/datatypes/Event.java
@@ -0,0 +1,77 @@
+package baguette.datatypes;
+
+import baguette.Parser;
+import java.time.LocalDateTime;
+
+/**
+ * Represents an event task in the Baguette application.
+ * Inherits from the Task class and includes start and end times.
+ */
+public class Event extends Task {
+ protected LocalDateTime from;
+ protected LocalDateTime to;
+
+ /**
+ * Constructs a new Event task with the specified description, start time, and end time.
+ *
+ * @param description The description of the event.
+ * @param from The start date and time in string format, which is parsed into a LocalDateTime object.
+ * @param to The end date and time in string format, which is parsed into a LocalDateTime object.
+ */
+ public Event(String description, String from, String to) {
+ super(description);
+ this.from = Parser.parseDateTime(from);
+ this.to = Parser.parseDateTime(to);
+ }
+
+ /**
+ * Constructs a new Event task with the specified description, start time, and end time.
+ *
+ * @param description The description of the event.
+ * @param from The start date and time in LocalDateTime format, which is parsed into a LocalDateTime object.
+ * @param to The end date and time in LocalDateTime format, which is parsed into a LocalDateTime object.
+ */
+ public Event(String description, LocalDateTime from, LocalDateTime to) {
+ super(description);
+ this.from = from;
+ this.to = to;
+ }
+
+ /**
+ * Retrieves the formatted start date and time of the event.
+ *
+ * @return A string representation of the event's start time.
+ */
+ public String getFrom() {
+ return Parser.toStringDateTime(from);
+ }
+
+ /**
+ * Retrieves the formatted end date and time of the event.
+ *
+ * @return A string representation of the event's end time.
+ */
+ public String getTo() {
+ return Parser.toStringDateTime(to);
+ }
+
+ /**
+ * Retrieves the type identifier for the event task.
+ *
+ * @return A string representing the task type, "[E]" for event.
+ */
+ public String getType() {
+ return "[E]";
+ }
+
+ /**
+ * Returns a string representation of the event task, including its description, start time, and end time.
+ *
+ * @return A formatted string representation of the task.
+ */
+
+ @Override
+ public String toString() {
+ return super.toString() + " (from: " + getFrom() + ", to: " + getTo() + ")";
+ }
+}
diff --git a/src/main/java/baguette/datatypes/Task.java b/src/main/java/baguette/datatypes/Task.java
new file mode 100644
index 000000000..8bd853e84
--- /dev/null
+++ b/src/main/java/baguette/datatypes/Task.java
@@ -0,0 +1,69 @@
+package baguette.datatypes;
+
+/**
+ * Serves as a base class for specific task types such as Todo, Deadline, and Event.
+ */
+public abstract class Task {
+ protected String desc;
+ protected boolean isDone;
+
+ /**
+ * Constructs a new Task with the specified description.
+ * By default, the task is not completed.
+ *
+ * @param description The description of the task.
+ */
+ public Task(String description) {
+ this.desc = description;
+ this.isDone = false;
+ }
+
+ /**
+ * Marks the task as completed.
+ */
+ public void mark() {
+ this.isDone = true;
+ }
+
+ /**
+ * Unmarks the task, setting it back to incomplete.
+ */
+ public void unmark() {
+ this.isDone = false;
+ }
+
+ /**
+ * Retrieves the completion status of the task.
+ *
+ * @return True if the task is done, false otherwise.
+ */
+ public boolean getIsDone() {
+ return isDone;
+ }
+
+ /**
+ * Retrieves the description of the task.
+ *
+ * @return The task description.
+ */
+ public String getDesc() {
+ return desc;
+ }
+
+ /**
+ * Retrieves the type identifier for the specific task type.
+ *
+ * @return A string representing the task type.
+ */
+ public abstract String getType();
+
+ /**
+ * Returns a string representation of the task, including its type, status, and description.
+ *
+ * @return A formatted string representation of the task.
+ */
+ @Override
+ public String toString() {
+ return getType() + (isDone ? "[✓] " : "[ ] ") + desc;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/baguette/datatypes/Todo.java b/src/main/java/baguette/datatypes/Todo.java
new file mode 100644
index 000000000..5d5436a06
--- /dev/null
+++ b/src/main/java/baguette/datatypes/Todo.java
@@ -0,0 +1,26 @@
+package baguette.datatypes;
+
+/**
+ * Represents a Todo task in the Baguette application.
+ * Inherits from the Task class and does not have any additional constraints.
+ */
+public class Todo extends Task {
+ /**
+ * Constructs a new Todo task with the specified description.
+ *
+ * @param description The description of the task.
+ */
+ public Todo(String description) {
+ super(description);
+ }
+
+ /**
+ * Retrieves the type identifier for the Todo task.
+ *
+ * @return A string representing the task type, "[T]" for Todo.
+ */
+ @Override
+ public String getType() {
+ return "[T]";
+ }
+}