diff --git a/.gitignore b/.gitignore index f69985ef1f..a3bbcefcae 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,7 @@ src/main/resources/docs/ *.iml bin/ -/text-ui-test/ACTUAL.txt -text-ui-test/EXPECTED-UNIX.TXT +/text-duke.ui-test/ACTUAL.txt +text-duke.ui-test/EXPECTED-UNIX.TXT + +main.jar \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..fdce4e149d --- /dev/null +++ b/build.gradle @@ -0,0 +1,63 @@ + +plugins { + id 'java' + id 'application' + id 'checkstyle' + id 'com.github.johnrengelman.shadow' version '5.1.0' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.jetbrains:annotations:20.1.0' + String javaFxVersion = '11' + + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux' + + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' +} + +test { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed" + + showExceptions true + exceptionFormat "full" + showCauses true + showStackTraces true + showStandardStreams = false + } +} + +application { + mainClassName = "duke.Launcher" +} + +shadowJar { + archiveBaseName = "duke" + archiveClassifier = null +} + +checkstyle { + toolVersion = '8.29' +} + +run{ + standardInput = System.in +} \ No newline at end of file diff --git a/config/checkstyle/Checkstyle.xml b/config/checkstyle/Checkstyle.xml new file mode 100644 index 0000000000..7951f3a6c4 --- /dev/null +++ b/config/checkstyle/Checkstyle.xml @@ -0,0 +1,398 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/checkstyle/Suppressions.xml b/config/checkstyle/Suppressions.xml new file mode 100644 index 0000000000..135ea49ee0 --- /dev/null +++ b/config/checkstyle/Suppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 8077118ebe..aa8dd6c1ab 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,28 +2,184 @@ ## Features -### Feature-ABC +#### Interactive Task List -Description of the feature. +Why keep track of your tasks in your head when a bot can do it for you? +Duke separates your tasks into three categories: todo, deadline and event. The Duke task list allows you to add, delete, find, view and update tasks. Then, when you're done with a task, simply mark it as done. -### Feature-XYZ +## Usage -Description of the feature. +### Viewing Help: `help` -## Usage +Displays a message which contains all the commands of the Duke bot and their usage. + +Example of usage: + +`help` + +Expected outcome: + +A message that lists all the commands of the bot. + + +### Viewing tasks: `list` + +Lists all the tasks in the task list. + +Example of usage: + +`list` + +Expected outcome: + +A message that lists all the tasks in the user's task list. + +``` +Here are the tasks in your list: + 1. [T][] borrow book + 2. [E][] family dinner (at: Sep 12 2021) + 3. [D][] assignment 1 (by: Sep 14 2021) +``` + +### Adding a todo: `todo` + +Adds a todo task to the task list. + +Format: + +`todo ` + +Example of usage: + +`todo borrow book` + +Expected outcome: + +A todo is added to the task list and a message is returned to confirm the addition of the todo task. + +``` +Got it. I've added this task: + [T][] borrow book +Now you have 1 task in the list. +``` + +### Adding a deadline: `deadline` + +Adds a deadline task to the task list. + +Format: + +`deadline /by ` + +Example of usage: + +`deadline assignment 1 /by 2021-09-14` + +Expected outcome: + +A deadline is added to the task list and a message is returned to confirm the addition of the deadline task. + +``` +Got it. I've added this task: + [D][] assignment 1 (by: Sep 14 2021) +Now you have 1 task in the list. +``` + +### Adding an event: `event` + +Adds an event task to the task list. -### `Keyword` - Describe action +Format: -Describe the action and its outcome. +`event /at ` Example of usage: -`keyword (optional arguments)` +`event family dinner /by 2021-09-12` Expected outcome: -Description of the outcome. +An event is added to the task list and a message is returned to confirm the addition of the event task. ``` -expected output +Got it. I've added this task: + [E][] family dinner (by: Sep 12 2021) +Now you have 1 task in the list. ``` + +### Finding tasks: `find` + +Finds all the tasks that contain the user's search query. + +Format: + +`find ` + +Example of usage: + +`find book` + +Expected outcome: + +A message that lists all the tasks that fit the user's search query. + +``` +Here are the matching tasks in your list: + [T][] borrow book +``` + +### Deleting a task: `delete` + +Deletes the specified task from the task list and returns a message to confirm the deletion. + +Format: + +`delete ` + +Example of usage: + +`delete 1` + +Expected outcome: + +The task is deleted from the task list and a message is returned to confirm the deletion. + +``` +Noted. I've removed the this task: + [T][] borrow book +Now you have 0 tasks in the list. +``` + +### Updating a task: `delete` + +Updates the specified task and returns a message to confirm the update. + +Format: + +`update ` + +Example of usage: + +`udpate 1 assignment 1 and 2 /by 2021-09-20` + +Expected outcome: + +The task is updated and a message is returned to confirm the update. + +``` +Your task has been updated! + [D][] assignment 1 and 2 (by: Sep 20 2021) +``` + +### Exiting: `exit` or `bye` + +Closes the application + + +Example of usage: + +`exit` + +Expected outcome: + +The application closes diff --git a/docs/Ui.png b/docs/Ui.png new file mode 100644 index 0000000000..807305c7a6 Binary files /dev/null and b/docs/Ui.png differ diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000000..259a24e4d2 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-tactile \ No newline at end of file diff --git a/gradle.bat b/gradle.bat new file mode 100644 index 0000000000..a14a3d051d --- /dev/null +++ b/gradle.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..f3d88b1c2f Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..c14115d7e0 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334cc..0000000000 --- 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/duke/Duke.java b/src/main/java/duke/Duke.java new file mode 100644 index 0000000000..1b63caff93 --- /dev/null +++ b/src/main/java/duke/Duke.java @@ -0,0 +1,40 @@ +package duke; + +import duke.command.Command; +import duke.exception.DukeException; +import duke.parser.Parser; +import duke.storage.Storage; +import duke.task.TaskList; + + +public class Duke { + + private Storage storage; + private TaskList taskList; + + /** + * Constructor for Duke object. + * + * @param filePath the file path of the document which stores the users tasks. + */ + public Duke(String filePath) { + this.storage = new Storage(filePath); + this.taskList = this.storage.load(); + } + + /** + * Gets the program output give the user input. + * + * @param input User input + * @return Duke's response + */ + public String getResponse(String input) { + try { + Command command = Parser.parse(input); + return command.execute(this.taskList, input, this.storage); + } catch (DukeException e) { + return e.getMessage(); + } + } + +} diff --git a/src/main/java/duke/Launcher.java b/src/main/java/duke/Launcher.java new file mode 100644 index 0000000000..e4ef6b4628 --- /dev/null +++ b/src/main/java/duke/Launcher.java @@ -0,0 +1,12 @@ +package duke; + +import javafx.application.Application; + +/** + * A launcher class to workaround classpath issues. + */ +public class Launcher { + public static void main(String[] args) { + Application.launch(Main.class, args); + } +} diff --git a/src/main/java/duke/Main.java b/src/main/java/duke/Main.java new file mode 100644 index 0000000000..1564a08e79 --- /dev/null +++ b/src/main/java/duke/Main.java @@ -0,0 +1,33 @@ +package duke; + +import java.io.IOException; + +import duke.gui.MainWindow; +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +/** + * A GUI for Duke using FXML. + */ +public class Main extends Application { + + private Duke duke = new Duke("Data\\taskList.txt"); + + @Override + public void start(Stage stage) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml")); + AnchorPane ap = fxmlLoader.load(); + Scene scene = new Scene(ap); + stage.setScene(scene); + stage.setTitle("Duke - your personal task list"); + fxmlLoader.getController().setDuke(duke); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/duke/command/Command.java b/src/main/java/duke/command/Command.java new file mode 100644 index 0000000000..6e36775740 --- /dev/null +++ b/src/main/java/duke/command/Command.java @@ -0,0 +1,25 @@ +package duke.command; + +import duke.exception.DukeException; +import duke.storage.Storage; +import duke.task.TaskList; + +/** + * Represents a command for Duke bot. + * + * @author Li Ming Gao Rickie + */ +public abstract class Command { + + /** + * Executes the command. + * + * @param taskList the list of user tasks. + * @param input the user's input. + * @param storage the storage which holds all the user's data. + * @return a string that states the outcome of the execution of the command. + * @throws DukeException if input is invalid. + */ + public abstract String execute(TaskList taskList, String input, Storage storage) throws DukeException; + +} diff --git a/src/main/java/duke/command/DeadlineCommand.java b/src/main/java/duke/command/DeadlineCommand.java new file mode 100644 index 0000000000..f85dcc2231 --- /dev/null +++ b/src/main/java/duke/command/DeadlineCommand.java @@ -0,0 +1,30 @@ +package duke.command; + +import duke.exception.DukeException; +import duke.storage.Storage; +import duke.task.Deadline; +import duke.task.TaskList; +import duke.ui.Ui; + +public class DeadlineCommand extends Command { + + /** + * {@inheritDoc} + */ + @Override + public String execute(TaskList taskList, String input, Storage storage) throws DukeException { + try { + if (input.replaceAll("\\s", "").length() == 8) { + throw new DukeException(DukeException.Type.EmptyDeadline); + } else { + String[] processedInput = input.substring(9).split(" /by ", 2); + Deadline d = new Deadline(processedInput[0], processedInput[1]); + storage.addTask(d); + taskList.addTask(d); + return Ui.printTaskCreated(d, taskList.getNumTasks()); + } + } catch (DukeException e) { + return e.getMessage(); + } + } +} diff --git a/src/main/java/duke/command/DeleteCommand.java b/src/main/java/duke/command/DeleteCommand.java new file mode 100644 index 0000000000..e1a69a08ba --- /dev/null +++ b/src/main/java/duke/command/DeleteCommand.java @@ -0,0 +1,48 @@ +package duke.command; + +import duke.exception.DukeException; +import duke.storage.Storage; +import duke.task.Task; +import duke.task.TaskList; +import duke.ui.Ui; + +public class DeleteCommand extends Command { + + /** + * {@inheritDoc} + */ + @Override + public String execute(TaskList taskList, String input, Storage storage) throws DukeException { + try { + if (input.replaceAll("\\s", "").length() == 6) { + throw new DukeException(DukeException.Type.EmptyDelete); + } else { + return processDelete(taskList, input, storage); + } + } catch (DukeException e) { + return e.getMessage(); + } + } + + /** + * Deletes the given task, saves task list to storage and returns confirmation string. + * + * @param taskList the list of user tasks. + * @param input the user's input; + * @param storage the storage which holds all the user's data. + * @return + */ + private String processDelete(TaskList taskList, String input, Storage storage) { + try { + int index = Integer.parseInt(input.substring(7)); + if (index < 1 || taskList.getNumTasks() < index) { + throw new DukeException(DukeException.Type.InvalidTaskNumber); + } else { + Task task = taskList.removeTask(index, storage); + return Ui.printTaskDeleted(task, taskList.getNumTasks()); + } + } catch (DukeException e) { + return e.getMessage(); + } + } +} diff --git a/src/main/java/duke/command/DoneCommand.java b/src/main/java/duke/command/DoneCommand.java new file mode 100644 index 0000000000..0786389850 --- /dev/null +++ b/src/main/java/duke/command/DoneCommand.java @@ -0,0 +1,48 @@ +package duke.command; + +import duke.exception.DukeException; +import duke.storage.Storage; +import duke.task.Task; +import duke.task.TaskList; +import duke.ui.Ui; + +public class DoneCommand extends Command { + + /** + * {@inheritDoc} + */ + @Override + public String execute(TaskList taskList, String input, Storage storage) { + try { + if (input.replaceAll("\\s", "").length() == 4) { + throw new DukeException(DukeException.Type.EmptyDone); + } else { + return processDone(taskList, input, storage); + } + } catch (DukeException e) { + return e.getMessage(); + } + } + + /** + * Marks the task as done, saves task list to storage and returns confirmation string. + * + * @param taskList the list of user tasks. + * @param input the user's input; + * @param storage the storage which holds all the user's data. + * @return + */ + private String processDone(TaskList taskList, String input, Storage storage) { + try { + int index = Integer.parseInt(input.substring(5)); + if (index < 1 || taskList.getNumTasks() < index) { + throw new DukeException(DukeException.Type.InvalidTaskNumber); + } else { + Task task = taskList.markTaskAsDone(index, storage); + return Ui.printTaskDone(task); + } + } catch (DukeException e) { + return e.getMessage(); + } + } +} diff --git a/src/main/java/duke/command/EventCommand.java b/src/main/java/duke/command/EventCommand.java new file mode 100644 index 0000000000..cd274c5cf7 --- /dev/null +++ b/src/main/java/duke/command/EventCommand.java @@ -0,0 +1,30 @@ +package duke.command; + +import duke.exception.DukeException; +import duke.storage.Storage; +import duke.task.Event; +import duke.task.TaskList; +import duke.ui.Ui; + +public class EventCommand extends Command { + + /** + * {@inheritDoc} + */ + @Override + public String execute(TaskList taskList, String input, Storage storage) throws DukeException { + try { + if (input.replaceAll("\\s", "").length() == 5) { + throw new DukeException(DukeException.Type.EmptyEvent); + } else { + String[] processedInput = input.substring(6).split(" /at ", 2); + Event ev = new Event(processedInput[0], processedInput[1]); + storage.addTask(ev); + taskList.addTask(ev); + return Ui.printTaskCreated(ev, taskList.getNumTasks()); + } + } catch (DukeException e) { + return e.getMessage(); + } + } +} diff --git a/src/main/java/duke/command/ExitCommand.java b/src/main/java/duke/command/ExitCommand.java new file mode 100644 index 0000000000..6f362b1c2e --- /dev/null +++ b/src/main/java/duke/command/ExitCommand.java @@ -0,0 +1,19 @@ +package duke.command; + +import duke.exception.DukeException; +import duke.storage.Storage; +import duke.task.TaskList; +import duke.ui.Ui; +import javafx.application.Platform; + +public class ExitCommand extends Command { + + /** + * {@inheritDoc} + */ + @Override + public String execute(TaskList taskList, String input, Storage storage) throws DukeException { + Platform.exit(); + return Ui.printGoodbyeMessage(); + } +} diff --git a/src/main/java/duke/command/FindCommand.java b/src/main/java/duke/command/FindCommand.java new file mode 100644 index 0000000000..a7b530fbf3 --- /dev/null +++ b/src/main/java/duke/command/FindCommand.java @@ -0,0 +1,27 @@ +package duke.command; + +import duke.exception.DukeException; +import duke.storage.Storage; +import duke.task.TaskList; +import duke.ui.Ui; + +public class FindCommand extends Command { + + /** + * {@inheritDoc} + */ + @Override + public String execute(TaskList taskList, String input, Storage storage) throws DukeException { + try { + if (input.replaceAll("\\s", "").length() == 4) { + throw new DukeException(DukeException.Type.EmptyFind); + } else { + String query = input.substring(5).toLowerCase(); + TaskList matchingTasks = taskList.findTasks(query); + return Ui.printTasksFound(matchingTasks); + } + } catch (DukeException e) { + return e.getMessage(); + } + } +} diff --git a/src/main/java/duke/command/HelpCommand.java b/src/main/java/duke/command/HelpCommand.java new file mode 100644 index 0000000000..e6fb3ece5b --- /dev/null +++ b/src/main/java/duke/command/HelpCommand.java @@ -0,0 +1,17 @@ +package duke.command; + +import duke.exception.DukeException; +import duke.storage.Storage; +import duke.task.TaskList; +import duke.ui.Ui; + +public class HelpCommand extends Command { + + /** + * {@inheritDoc} + */ + @Override + public String execute(TaskList taskList, String input, Storage storage) throws DukeException { + return Ui.printHelpMessage(); + } +} diff --git a/src/main/java/duke/command/ListCommand.java b/src/main/java/duke/command/ListCommand.java new file mode 100644 index 0000000000..abb9e461d8 --- /dev/null +++ b/src/main/java/duke/command/ListCommand.java @@ -0,0 +1,17 @@ +package duke.command; + +import duke.exception.DukeException; +import duke.storage.Storage; +import duke.task.TaskList; +import duke.ui.Ui; + +public class ListCommand extends Command { + + /** + * {@inheritDoc} + */ + @Override + public String execute(TaskList taskList, String input, Storage storage) throws DukeException { + return Ui.printTaskList(taskList); + } +} diff --git a/src/main/java/duke/command/TodoCommand.java b/src/main/java/duke/command/TodoCommand.java new file mode 100644 index 0000000000..7ec9feae59 --- /dev/null +++ b/src/main/java/duke/command/TodoCommand.java @@ -0,0 +1,29 @@ +package duke.command; + +import duke.exception.DukeException; +import duke.storage.Storage; +import duke.task.TaskList; +import duke.task.Todo; +import duke.ui.Ui; + +public class TodoCommand extends Command { + + /** + * {@inheritDoc} + */ + @Override + public String execute(TaskList taskList, String input, Storage storage) throws DukeException { + try { + if (input.replaceAll("\\s", "").length() == 4) { + throw new DukeException(DukeException.Type.EmptyTodo); + } else { + Todo t = new Todo(input.substring(5)); + storage.addTask(t); + taskList.addTask(t); + return Ui.printTaskCreated(t, taskList.getNumTasks()); + } + } catch (DukeException e) { + return e.getMessage(); + } + } +} diff --git a/src/main/java/duke/command/UpdateCommand.java b/src/main/java/duke/command/UpdateCommand.java new file mode 100644 index 0000000000..053c895347 --- /dev/null +++ b/src/main/java/duke/command/UpdateCommand.java @@ -0,0 +1,40 @@ +package duke.command; + +import duke.exception.DukeException; +import duke.storage.Storage; +import duke.task.Task; +import duke.task.TaskList; +import duke.ui.Ui; + +public class UpdateCommand extends Command { + + /** + * {@inheritDoc} + */ + @Override + public String execute(TaskList taskList, String input, Storage storage) throws DukeException { + try { + if (input.replaceAll("\\s", "").length() <= 7) { + throw new DukeException(DukeException.Type.EmptyUpdate); + } else { + return processUpdate(taskList, input, storage); + } + } catch (DukeException e) { + return e.getMessage(); + } + } + + private String processUpdate(TaskList taskList, String input, Storage storage) { + try { + int taskNum = Integer.parseInt(input.substring(7, 8)); + if (taskList.getNumTasks() < taskNum) { + throw new DukeException(DukeException.Type.InvalidTaskNumber); + } else { + Task task = taskList.updateTask(taskNum, input.substring(9), storage); + return Ui.printTaskUpdated(task); + } + } catch (DukeException e) { + return e.getMessage(); + } + } +} diff --git a/src/main/java/duke/exception/DukeException.java b/src/main/java/duke/exception/DukeException.java new file mode 100644 index 0000000000..37cb204ea3 --- /dev/null +++ b/src/main/java/duke/exception/DukeException.java @@ -0,0 +1,66 @@ +package duke.exception; +public class DukeException extends RuntimeException { + + public enum Type { + EmptyDone { + @Override + public String getMessage() { + return "(O_O) OOPS!!! You haven't specified the task you have completed."; + } + }, + EmptyDelete { + @Override + public String getMessage() { + return "(O_O) OOPS!!! You haven't specified the task you want to delete."; + } + }, + InvalidTaskNumber { + @Override + public String getMessage() { + return "(O_O) OOPS!!! The task number you have specified is invalid."; + } + }, + EmptyTodo { + @Override + public String getMessage() { + return "(O_O) OOPS!!! The description of a todo cannot be empty."; + } + }, + EmptyEvent { + @Override + public String getMessage() { + return "(O_O) OOPS!!! The description of an event cannot be empty."; + } + }, + EmptyDeadline { + @Override + public String getMessage() { + return "(O_O) OOPS!!! The description of a deadline cannot be empty."; + } + }, + EmptyFind { + @Override + public String getMessage() { + return "(O_O) OOPS!!! The query for find cannot be empty."; + } + }, + EmptyUpdate { + @Override + public String getMessage() { + return "(O_O) OOPS!!! The update cannot be empty."; + } + }, + InvalidCommand { + @Override + public String getMessage() { + return "(O_O) OOPS!!! I'm sorry, but I don't know what that means :-("; + } + }; + + public abstract String getMessage(); + } + + public DukeException(Type type) { + super(type.getMessage()); + } +} diff --git a/src/main/java/duke/gui/DialogBox.java b/src/main/java/duke/gui/DialogBox.java new file mode 100644 index 0000000000..cc7f6b1b70 --- /dev/null +++ b/src/main/java/duke/gui/DialogBox.java @@ -0,0 +1,60 @@ +package duke.gui; + +import java.io.IOException; +import java.util.Collections; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import javafx.scene.shape.Circle; + +public class DialogBox extends HBox { + @FXML + private Label dialog; + @FXML + private ImageView displayPicture; + private Circle clip = new Circle(50, 50, 45); + + private DialogBox(String text, Image img) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml")); + fxmlLoader.setController(this); + fxmlLoader.setRoot(this); + fxmlLoader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + dialog.setText(text); + dialog.setAlignment(Pos.CENTER); + displayPicture.setImage(img); + displayPicture.setClip(clip); + } + + /** + * Flips the dialog box such that the ImageView is on the left and text on the right. + */ + private void flip() { + ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); + Collections.reverse(tmp); + getChildren().setAll(tmp); + setAlignment(Pos.TOP_LEFT); + } + + public static DialogBox getUserDialog(String text, Image img) { + return new DialogBox(text, img); + } + + public static DialogBox getDukeDialog(String text, Image img) { + var db = new DialogBox(text, img); + db.flip(); + return db; + } +} diff --git a/src/main/java/duke/gui/MainWindow.java b/src/main/java/duke/gui/MainWindow.java new file mode 100644 index 0000000000..6da8bd3b68 --- /dev/null +++ b/src/main/java/duke/gui/MainWindow.java @@ -0,0 +1,56 @@ +package duke.gui; + +import duke.Duke; +import duke.ui.Ui; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; +/** + * Controller for MainWindow. Provides the layout for the other controls. + */ +public class MainWindow extends AnchorPane { + @FXML + private ScrollPane scrollPane; + @FXML + private VBox dialogContainer; + @FXML + private TextField userInput; + @FXML + private Button sendButton; + + private Duke duke; + + private Image userImage = new Image(this.getClass().getResourceAsStream("/images/DaUser.png")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png")); + + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + dialogContainer.getChildren().addAll(DialogBox.getDukeDialog(Ui.printWelcomeMessage(), dukeImage)); + } + + public void setDuke(Duke d) { + duke = d; + } + + /** + * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + @FXML + private void handleUserInput() { + String input = userInput.getText(); + assert input != null; + String response = duke.getResponse(input); + assert response != null; + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage), + DialogBox.getDukeDialog(response, dukeImage) + ); + userInput.clear(); + } +} diff --git a/src/main/java/duke/parser/Parser.java b/src/main/java/duke/parser/Parser.java new file mode 100644 index 0000000000..29ee2a2e1f --- /dev/null +++ b/src/main/java/duke/parser/Parser.java @@ -0,0 +1,65 @@ +package duke.parser; + +import duke.command.Command; +import duke.command.DeadlineCommand; +import duke.command.DeleteCommand; +import duke.command.DoneCommand; +import duke.command.EventCommand; +import duke.command.ExitCommand; +import duke.command.FindCommand; +import duke.command.HelpCommand; +import duke.command.ListCommand; +import duke.command.TodoCommand; +import duke.command.UpdateCommand; +import duke.exception.DukeException; + +public class Parser { + + /** + * Returns the command inputted by the user. + * + * @param input user's input. + * @return command + */ + public static Command parse(String input) { + String[] commandWord = input.split("\\s+"); + Command command; + switch (commandWord[0]) { + case "help": + command = new HelpCommand(); + break; + case "list": + command = new ListCommand(); + break; + case "done": + command = new DoneCommand(); + break; + case "delete": + command = new DeleteCommand(); + break; + case "todo": + command = new TodoCommand(); + break; + case "event": + command = new EventCommand(); + break; + case "deadline": + command = new DeadlineCommand(); + break; + case "find": + command = new FindCommand(); + break; + case "update": + command = new UpdateCommand(); + break; + case "bye": + case "exit": + command = new ExitCommand(); + break; + default: + throw new DukeException(DukeException.Type.InvalidCommand); + } + return command; + } + +} diff --git a/src/main/java/duke/storage/Storage.java b/src/main/java/duke/storage/Storage.java new file mode 100644 index 0000000000..059a1ae887 --- /dev/null +++ b/src/main/java/duke/storage/Storage.java @@ -0,0 +1,170 @@ +package duke.storage; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Scanner; + +import duke.task.Deadline; +import duke.task.Event; +import duke.task.Task; +import duke.task.TaskList; +import duke.task.Todo; + +public class Storage { + + protected static File taskFile; + protected String filePath; + protected TaskList taskList; + + /** + * Constructor for storage. + * + * @param filePath the file path to the text file that stores the user's tasks. + */ + public Storage(String filePath) { + this.filePath = filePath; + this.taskFile = new File(filePath); + this.taskList = new TaskList(); + } + + /** + * Returns user's list of tasks + * + * @return Task List + */ + public TaskList load() { + assert taskFile.exists() : "there is no such file"; + try { + taskFile.getParentFile().mkdirs(); + if (!taskFile.createNewFile()) { + Scanner fileReader = new Scanner(taskFile); + this.taskList = loadList(this.taskList, fileReader); + fileReader.close(); + } + } catch (IOException e) { + System.out.println("An error occurred."); + e.printStackTrace(); + } + return taskList; + } + + private Todo makeTodo(String description, char done) { + Todo t = new Todo(description); + if (done == '1') { + t.markAsDone(); + } + return t; + } + + private Deadline makeDeadline(String[] processedInput) { + Deadline d = new Deadline(processedInput[2], processedInput[3]); + if (processedInput[1].equals("1")) { + d.markAsDone(); + } + return d; + } + + private Event makeEvent(String[] processedInput) { + Event ev = new Event(processedInput[2], processedInput[3]); + if (processedInput[1].equals("1")) { + ev.markAsDone(); + } + return ev; + } + + private TaskList loadList(TaskList taskList, Scanner fileReader) { + while (fileReader.hasNextLine()) { + String line = fileReader.nextLine(); + String[] processedInput = line.split(" \\| "); + if (processedInput[0].equals("T")) { + Todo t = makeTodo(line.substring(8), line.charAt(4)); + taskList.addTask(t); + } else if (processedInput[0].equals("D")) { + Deadline d = makeDeadline(processedInput); + taskList.addTask(d); + } else { + Event e = makeEvent(processedInput); + taskList.addTask(e); + } + } + return taskList; + } + + /** + * Saves the users tasks to a text file. + * + * @param taskList the list of user tasks. + */ + public void save(ArrayList taskList) { + File list = new File(this.filePath); + list.delete(); + try { + FileWriter writer = new FileWriter(this.filePath); + writeToFile(taskList, writer); + writer.close(); + } catch (IOException e) { + System.out.println("An error occurred."); + e.printStackTrace(); + } + } + + private void writeToFile(ArrayList taskList, FileWriter writer) throws IOException { + try { + assert taskList != null; + for (int i = 0; i < taskList.size(); i++) { + Task task = taskList.get(i); + String isDone = task.getIsDone() ? "1" : "0"; + if (task instanceof Todo) { + writer.write(getTodoString(task, isDone) + "\n"); + } else if (task instanceof Deadline) { + writer.write(getDeadlineString(task, isDone) + "\n"); + } else if (task instanceof Event) { + writer.write(getEventString(task, isDone) + "\n"); + } + } + } catch (IOException e) { + System.out.println("An error occurred."); + e.printStackTrace(); + } + } + + /** + * Writes new task to file. + * + * @param task the task to be added to the file. + */ + public void addTask(Task task) { + assert task != null; + try { + FileWriter writer = new FileWriter(this.filePath, true); + if (task instanceof Todo) { + writer.write(getTodoString(task, "0") + "\n"); + } else if (task instanceof Deadline) { + writer.write(getDeadlineString(task, "0") + "\n"); + } else if (task instanceof Event) { + writer.write(getEventString(task, "0") + "\n"); + } + writer.close(); + } catch (IOException e) { + System.out.println("An error occurred."); + e.printStackTrace(); + } + } + + private String getTodoString(Task task, String isDone) { + return "T | " + isDone + " | " + task.getDescription(); + } + + private String getDeadlineString(Task task, String isDone) { + Deadline d = (Deadline) task; + return "D | " + isDone + " | " + d.getDescription() + " | " + d.getBy(); + } + + private String getEventString(Task task, String isDone) { + Event e = (Event) task; + return "E | " + isDone + " | " + e.getDescription() + " | " + e.getAt(); + } + +} diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java new file mode 100644 index 0000000000..3205f7dfee --- /dev/null +++ b/src/main/java/duke/task/Deadline.java @@ -0,0 +1,62 @@ +package duke.task; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +public class Deadline extends Task { + + protected String by; + protected LocalDate date; + + /** + * Constructor for a Deadline object. + * + * @param description the description of the deadline task. + * @param by the due date of the task. + */ + public Deadline(String description, String by) { + super(description); + this.by = by; + String[] d = by.split("-"); + if (d.length == 3) { + date = LocalDate.parse(by); + } + } + + /** + * Updates the description and due date of the deadline. + * + * @param input user's input for updating the deadline. + * @return new Deadline with updated description and due date. + */ + @Override + public Deadline update(String input) { + if (input.contains("/by")) { + String[] splitInput = input.split(" /by ", 2); + return new Deadline(splitInput[0], splitInput[1]); + } else { + return new Deadline(input, this.by); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + if (this.date != null) { + return "[D]" + super.toString() + " (by: " + + this.date.format(DateTimeFormatter.ofPattern("MMM d yyyy")) + ")"; + } else { + return "[D]" + super.toString() + " (by: " + by + ")"; + } + } + + /** + * Returns the due date of the deadline. + * + * @return the value of by. + */ + public String getBy() { + return this.by; + } +} diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java new file mode 100644 index 0000000000..d209e6f1da --- /dev/null +++ b/src/main/java/duke/task/Event.java @@ -0,0 +1,62 @@ +package duke.task; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +public class Event extends Task { + + protected String at; + protected LocalDate date; + + /** + * Constructor for an event. + * + * @param description a description of the event. + * @param at the date the event is happening. + */ + public Event(String description, String at) { + super(description); + this.at = at; + String[] d = at.split("-"); + if (d.length == 3) { + date = LocalDate.parse(at); + } + } + + /** + * Updates the description and due date of the Event. + * + * @param input the description of the event task. + * @return new Event with updated description and due date. + */ + @Override + public Event update(String input) { + if (input.contains("/at")) { + String[] splitInput = input.split(" /at ", 2); + return new Event(splitInput[0], splitInput[1]); + } else { + return new Event(input, this.at); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + if (this.date != null) { + return "[E]" + super.toString() + " (at: " + + this.date.format(DateTimeFormatter.ofPattern("MMM d yyyy")) + ")"; + } else { + return "[E]" + super.toString() + " (at: " + this.at + ")"; + } + } + + /** + * Returns the date of the event. + * + * @return the value of at. + */ + public String getAt() { + return this.at; + } +} diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java new file mode 100644 index 0000000000..0de3b6ecab --- /dev/null +++ b/src/main/java/duke/task/Task.java @@ -0,0 +1,66 @@ +package duke.task; +public abstract class Task { + protected String description; + protected boolean isDone; + + /** + * Constructor for task object. + * + * @param description description of the task. + */ + public Task(String description) { + this.description = description; + this.isDone = false; + } + + /** + * Returns the status of the task (X for done, " " for not yet done). + * + * @return the icon that represents the status of the task. + */ + public String getStatusIcon() { + return (isDone ? "X" : " "); + } + + /** + * Marks the task as done. + */ + public void markAsDone() { + this.isDone = true; + } + + /** + * Returns a boolean representing the status of the task. + * + * @return the task's isDone value. + */ + public boolean getIsDone() { + return this.isDone; + } + + /** + * Updates the description and due date of the Event. + * + * @param input the description of the event task. + * @return new Task with updated description. + */ + public abstract Task update(String input); + + /** + * Returns the description of the task. + * + * @return the description of the task. + */ + public String getDescription() { + return this.description; + } + + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "[" + this.getStatusIcon() + "] " + this.description; + } +} diff --git a/src/main/java/duke/task/TaskList.java b/src/main/java/duke/task/TaskList.java new file mode 100644 index 0000000000..1ea9b52f55 --- /dev/null +++ b/src/main/java/duke/task/TaskList.java @@ -0,0 +1,106 @@ +package duke.task; + +import java.util.ArrayList; + +import duke.storage.Storage; + + +public class TaskList { + private final ArrayList taskList; + + /** + * Constructor for taskList. + */ + public TaskList() { + this.taskList = new ArrayList(); + } + + /** + * Adds a task to the task list. + * + * @param task the task to be added to the task list. + */ + public void addTask(Task task) { + this.taskList.add(task); + } + + /** + * Removes a task from the task list. + * + * @param taskNum the index of the task (1-indexed). + * @param storage the storage which holds all the user's data. + * @return string that confirms the removal of the task. + */ + public Task removeTask(int taskNum, Storage storage) { + Task task = getTask(taskNum); + this.taskList.remove(task); + storage.save(this.taskList); + return task; + } + + /** + * Marks task indicated by the task number as done. + * + * @param taskNum the index of the task (1-indexed). + * @param storage the storage which holds all the user's data. + * @return string that confirms the task has been marked done. + */ + public Task markTaskAsDone(int taskNum, Storage storage) { + Task task = getTask(taskNum); + assert task != null; + task.markAsDone(); + storage.save(this.taskList); + return task; + } + + /** + * Returns the task indicated by the task number. + * + * @param taskNum the index of the task (1-indexed). + * @return the task indicated by the task number. + */ + public Task getTask(int taskNum) { + return this.taskList.get(taskNum - 1); + } + + /** + * Returns a TaskList containing all the tasks that contain the search query. + * + * @param query the user's search query. + * @return a tasklist that contains all the tasks that contain the search query. + */ + public TaskList findTasks(String query) { + TaskList matchingTasks = new TaskList(); + for (Task t : this.taskList) { + if (t.getDescription().toLowerCase().contains(query)) { + matchingTasks.addTask(t); + } + } + return matchingTasks; + } + + /** + * Updates task given the task number and update, then updates file. + * + * @param taskNum the index of the task (1-indexed). + * @param update the update to be applied to the task. + * @param storage the storage which holds all the user's data. + */ + public Task updateTask(int taskNum, String update, Storage storage) { + Task task = getTask(taskNum); + task = task.update(update); + this.taskList.set(taskNum - 1, task); + assert this.taskList != null; + storage.save(this.taskList); + return task; + } + + /** + * Returns the number of tasks in the task list. + * + * @return the number of tasks in the task list. + */ + public int getNumTasks() { + return taskList.size(); + } +} diff --git a/src/main/java/duke/task/Todo.java b/src/main/java/duke/task/Todo.java new file mode 100644 index 0000000000..862751ac33 --- /dev/null +++ b/src/main/java/duke/task/Todo.java @@ -0,0 +1,32 @@ +package duke.task; + +public class Todo extends Task { + + /** + * Constructor for a todo object. + * + * @param description the description of the todo object. + */ + public Todo(String description) { + super(description); + } + + /** + * Updates the description of the Todo. + * + * @param description the description of the todo task. + * @return new Todo with updated description and due date. + */ + @Override + public Todo update(String description) { + return new Todo(description); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "[T]" + super.toString(); + } +} diff --git a/src/main/java/duke/ui/Ui.java b/src/main/java/duke/ui/Ui.java new file mode 100644 index 0000000000..ee9acffdb4 --- /dev/null +++ b/src/main/java/duke/ui/Ui.java @@ -0,0 +1,130 @@ +package duke.ui; + +import duke.task.Task; +import duke.task.TaskList; + +public class Ui { + + /** + * Returns the welcome message of the duke chat bot. + * + * @return welcome message. + */ + public static String printWelcomeMessage() { + return "Hello! I'm Duke" + "\n" + "What can I do for you?" + "\n"; + } + + /** + * Returns the goodbye message of the duke chat bot. + * + * @return goodbye message. + */ + public static String printGoodbyeMessage() { + return "Bye! Hope to see you soon!" + "\n"; + } + + /** + * Returns message that contains all the tasks in the user's task list. + * + * @param taskList the user's task list. + * @return string that contains all the tasks in the user's task list. + */ + public static String printTaskList(TaskList taskList) { + if (taskList.getNumTasks() == 0) { + return "You have no tasks in your list!\n"; + } else { + StringBuilder output = new StringBuilder("Here are the tasks in your list:\n"); + for (int i = 1; i <= taskList.getNumTasks(); i++) { + Task item = taskList.getTask(i); + output.append(String.format(" %d. %s\n", (i), item.toString())); + } + return output.toString(); + } + } + + /** + * Returns message that confirms the task has been marked done. + * + * @param task the task that has been completed. + * @return string that confirms the task has been marked done. + */ + public static String printTaskDone(Task task) { + return "Nice! I've marked this task as done:\n" + + String.format(" %s\n", task); + } + + /** + * Returns message that confirms the creation of the task. + * + * @param task the task taht has been created. + * @param numTasks the total number of tasks in the user's task list. + * @return + */ + public static String printTaskCreated(Task task, int numTasks) { + return "Got it. I've added this task:\n " + task.toString() + "\n" + + String.format("Now you have %d tasks in the list.\n", numTasks); + } + + /** + * Returns message that confirms the task has been deleted. + * + * @param task the task that has been deleted. + * @param numTasks the total number of tasks in the task list. + * @return string that confirms the deletion of the task. + */ + public static String printTaskDeleted(Task task, int numTasks) { + return "Noted. I've removed this task:\n" + + String.format(" %s\nNow you have %d tasks in the list.\n", + task.toString(), numTasks); + } + + /** + * Returns message that confirms the task has been updated. + * + * @param task the task that has been updated. + * @return string that confirms the update of the task. + */ + public static String printTaskUpdated(Task task) { + return "Your task has been updated!\n " + task; + } + + /** + * Returns message that contains all the tasks that match the user's query. + * + * @param matchingTasks the tasks that match the user's query. + * @return string that lists all the tasks that matches the user's query. + */ + public static String printTasksFound(TaskList matchingTasks) { + if (matchingTasks.getNumTasks() == 0) { + return "You have no matching tasks in your list!\n"; + } else { + StringBuilder output = new StringBuilder("Here are the matching tasks in your list:\n"); + for (int i = 1; i <= matchingTasks.getNumTasks(); i++) { + Task item = matchingTasks.getTask(i); + output.append(String.format(" %d. %s\n", i, item.toString())); + } + return output.toString(); + } + } + + /** + * Returns message explaining how to use the duke bot. + * + * @return message explaining how to use the duke bot.s + */ + public static String printHelpMessage() { + String helpMessage = "Try these commands: \n\n"; + helpMessage += "View help: help \n\n"; + helpMessage += "Listing all tasks: list \n\n"; + helpMessage += "Adding a todo: todo \n\n"; + helpMessage += "Adding a deadline: deadline /by \n\n"; + helpMessage += "Adding an event: event /at \n\n"; + helpMessage += "Deleting a task: delete \n\n"; + helpMessage += "Updating a task: update (/at or /by if applicable) " + + " \n\n"; + helpMessage += "Finding a task: find \n\n"; + helpMessage += "Exiting: exit or bye"; + return helpMessage; + } + +} diff --git a/src/main/resources/images/DaDuke.png b/src/main/resources/images/DaDuke.png new file mode 100644 index 0000000000..b74b55e1bb Binary files /dev/null and b/src/main/resources/images/DaDuke.png differ diff --git a/src/main/resources/images/DaUser.png b/src/main/resources/images/DaUser.png new file mode 100644 index 0000000000..353636795b Binary files /dev/null and b/src/main/resources/images/DaUser.png differ diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..aaa558c390 --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 0000000000..ea33cc7de6 --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + +