diff --git a/LICENSE b/LICENSE index 39b3478982c..1f0425b6b51 100644 --- a/LICENSE +++ b/LICENSE @@ -2,11 +2,11 @@ MIT License Copyright (c) 2016 Software Engineering Education - FOSS Resources -Permission is hereby granted, free of charge, to any person obtaining a copy +Permission is hereby granted, free of charge, to any client obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is +copies of the Software, and to permit clients to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all diff --git a/README.md b/README.md index 13f5c77403f..83c7c1fb3cf 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,25 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) +[![Java CI](https://github.com/AY2122S2-CS2103T-W13-1/tp/actions/workflows/gradle.yml/badge.svg?branch=master)](https://github.com/AY2122S2-CS2103T-W13-1/tp/actions/workflows/gradle.yml) ![Ui](docs/images/Ui.png) -* This is **a sample project for Software Engineering (SE) students**.
- Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. - * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. - * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. -* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. +## Introduction +Networkers is a **desktop app for managing contacts for networks, optimised for use via a Command Line Interface (CLI)** +while still having the benefits of a Graphical User Interface (GUI). +If you can type fast, Networkers can get your contact management tasks done faster than traditional GUI apps. + +## Getting Started +Learn how to get started with Networkers by going to our +[User’s guide](https://ay2122s2-cs2103t-w13-1.github.io/tp/UserGuide.html). + +## Contributing to Networkers +For more information on how to contribute code to our project, see our +[Developer’s guide](https://ay2122s2-cs2103t-w13-1.github.io/tp/DeveloperGuide.html). + +## About the Team +If you wish to know more about the team behind Networkers, see our +[About us](https://ay2122s2-cs2103t-w13-1.github.io/tp/AboutUs.html) page. + +## Acknowledgements +This project is based on the AddressBook-Level3 project created by the SE-EDU initiative. + +Libraries used: JavaFx, Jackson, JUnit5 diff --git a/build.gradle b/build.gradle index be2d2905dde..010514e7e83 100644 --- a/build.gradle +++ b/build.gradle @@ -40,6 +40,10 @@ task coverage(type: JacocoReport) { } } +run { + enableAssertions = true +} + dependencies { String jUnitVersion = '5.4.0' String javaFxVersion = '11' @@ -66,7 +70,7 @@ dependencies { } shadowJar { - archiveName = 'addressbook.jar' + archiveName = 'networkers.jar' } defaultTasks 'clean', 'test' diff --git a/commandUseCases.docx b/commandUseCases.docx new file mode 100644 index 00000000000..a5e1c62440d Binary files /dev/null and b/commandUseCases.docx differ diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..a95ba8a9d38 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -5,55 +5,58 @@ title: About Us We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg). -You can reach us at the email `seer[at]comp.nus.edu.sg` +You can reach us at the email `networkers@comp.nus.edu.sg` ## Project team -### John Doe - +### Isaac Tan -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] + -* Role: Project Advisor +[[github](https://github.com/iztanpy)] +[[portfolio](team/iztanpy.md)] -### Jane Doe +* Role: Developer +* Responsibilities: Added Procedure class + as well as updated and added new features + +### Teo Ming Huang - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/teominghuang)] +[[portfolio](team/teominghuang.md)] -* Role: Team Lead -* Responsibilities: UI +* Role: Developer +* Responsibilities: Connect Procedure to the different dependencies, and update existing AB3 commands to Networkers. -### Johnny Doe +### Alexander Er - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](https://github.com/aceszhenwei)] +[[portfolio](team/aceszhenwei.md)] * Role: Developer -* Responsibilities: Data +* Responsibilities: Update existing AB3 commands to Networkers as well as documentation -### Jean Doe +### Yong Gi Roh - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/robinrojh)] +[[portfolio](team/robinrojh.md)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: Update new features and documentations, and manage UI-related issues -### James Doe +### JB Jungbae Kim - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/jbkim1999)] +[[portfolio](team/jbkim1999.md)] * Role: Developer -* Responsibilities: UI +* Responsibilities: Code Quality, Manage PRs diff --git a/docs/DevOps.md b/docs/DevOps.md index 26354050fa4..c8f4dc17201 100644 --- a/docs/DevOps.md +++ b/docs/DevOps.md @@ -74,7 +74,7 @@ Any warnings or errors will be printed out to the console. Here are the steps to create a new release. -1. Update the version number in [`MainApp.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java). +1. Update the version number in [`MainApp.java`](https://github.com/AY2122S2-CS2103T-W13-1/tp/blob/master/src/main/java/seedu/address/MainApp.java). 1. Generate a fat JAR file using Gradle (i.e., `gradlew shadowJar`). 1. Tag the repo with the version number. e.g. `v0.1` 1. [Create a new release using GitHub](https://help.github.com/articles/creating-releases/). Upload the JAR file you created. diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..e8011438f8f 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -4,12 +4,35 @@ title: Developer Guide --- * Table of Contents {:toc} +-------------------------------------------------------------------------------------------------------------------- + +## **Introduction** + +### **Purpose** + +This developer's guide clarifies the project architecture as well as software design decisions for **Networkers**. +This guide will also look at how individual features are implemented in this project. + +*Networkers* is a **desktop app for managing contacts for network technicians, +optimised for use via a Command Line Interface** (CLI) +while still having the benefits of a Graphical User Interface (GUI). + +### **Intended Audience** + +The intended audience of this document would be +1. Developers who are keen to contribute to Networkers +2. Software testers who may need to understand the project + to carry out meaningful testing. +3. Developers who are interested in learning more + about the implementation of this project -------------------------------------------------------------------------------------------------------------------- ## **Acknowledgements** -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +* This project is based on the [AddressBook-Level3 project](https://se-education.org/addressbook-level3/) created by the SE-EDU initiative. + +* Libraries used: JavaFx, Jackson, JUnit5, PlantUML -------------------------------------------------------------------------------------------------------------------- @@ -23,7 +46,7 @@ Refer to the guide [_Setting up and getting started_](SettingUp.md).
-:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/se-edu/addressbook-level3/tree/master/docs/diagrams/) folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams. +:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/AY2122S2-CS2103T-W13-1/tp/tree/master/docs/diagrams) folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
### Architecture @@ -36,11 +59,11 @@ Given below is a quick overview of main components and how they interact with ea **Main components of the architecture** -**`Main`** has two classes called [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java). It is responsible for, +**`Main`** has two classes called [`Main`](https://github.com/AY2122S2-CS2103T-W13-1/tp/blob/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/AY2122S2-CS2103T-W13-1/tp/blob/master/src/main/java/seedu/address/MainApp.java). It is responsible for, * At app launch: Initializes the components in the correct sequence, and connects them up with each other. * At shut down: Shuts down the components and invokes cleanup methods where necessary. -[**`Commons`**](#common-classes) represents a collection of classes used by multiple other components. +[**`Commons`**](#common-classes): represents a collection of classes used by multiple other components. The rest of the App consists of four components. @@ -49,10 +72,19 @@ The rest of the App consists of four components. * [**`Model`**](#model-component): Holds the data of the App in memory. * [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk. +Within the DG, when used, these components will be color coded as such: + +`UI`: Green + +`Logic` : Blue + +`Model` : Red + +`Storage` : Yellow **How the architecture components interact with each other** -The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`. +The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `deleteClient 1`. @@ -69,24 +101,24 @@ The sections below give more details of each component. ### UI component -The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java) +**API**: [`Ui.java`](https://github.com/AY2122S2-CS2103T-W13-1/tp/blob/master/src/main/java/seedu/address/ui/Ui.java) ![Structure of the UI Component](images/UiClassDiagram.png) -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `ClientListPanel`, `ProcedureListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. -The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml) +The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/AY2122S2-CS2103T-W13-1/tp/blob/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/AY2122S2-CS2103T-W13-1/tp/blob/master/src/main/resources/view/MainWindow.fxml) The `UI` component, * executes user commands using the `Logic` component. * listens for changes to `Model` data so that the UI can be updated with the modified data. * keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands. -* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`. +* depends on some classes in the `Model` component, as it displays `Client` and `Procedure` objects residing in the `Model`. ### Logic component -**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java) +**API** : [`Logic.java`](https://github.com/AY2122S2-CS2103T-W13-1/tp/blob/master/src/main/java/seedu/address/logic/Logic.java) Here's a (partial) class diagram of the `Logic` component: @@ -95,14 +127,14 @@ Here's a (partial) class diagram of the `Logic` component: How the `Logic` component works: 1. When `Logic` is called upon to execute a command, it uses the `AddressBookParser` class to parse the user command. 1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to add a person). +1. The command can communicate with the `Model` when it is executed (e.g. to add a Client). 1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. -The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call. +The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("deleteClient 1")` API call. -![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) +![Interactions Inside the Logic Component for the `deleteClient 1` Command](images/DeleteSequenceDiagram.png) -
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. +
:information_source: **Note:** The lifeline for `DeleteClientCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command: @@ -114,28 +146,26 @@ How the parsing works: * All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. ### Model component -**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) +**API** : [`Model.java`](https://github.com/AY2122S2-CS2103T-W13-1/tp/blob/master/src/main/java/seedu/address/model/Model.java) - The `Model` component, -* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object). -* stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. +* stores the address book data i.e., all `Client` objects (which are contained in a `UniqueClientList` object). +* stores the currently 'selected' `Client` and `Procedure` objects (e.g., results of a search query) as separate _filtered_ lists which are exposed to outsiders as an unmodifiable `ObservableList` and an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. * stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects. * does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components) -
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
+
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Client` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Client` needing their own `Tag` objects.
- ### Storage component -**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) +**API** : [`Storage.java`](https://github.com/AY2122S2-CS2103T-W13-1/tp/blob/master/src/main/java/seedu/address/storage/Storage.java) @@ -154,89 +184,236 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa This section describes some noteworthy details on how certain features are implemented. -### \[Proposed\] Undo/redo feature +### Add Procedure (AddProc) -#### Proposed Implementation +This mechanism is facilitated by the `AddProcCommand`. +It extends from `Command` and takes in a new `Procedure` and `Index`, which points to the Client that it wishes to add the Procedure to. +It will interact with `Storage` to store the information about the new Procedure added. +This operation is exposed in the `Model` interface as `Model#setProcedures()`. -The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations: +In general, the `addProc` command is a command that takes in a string with a Client index and its relevant specified prefixes and parameters. +It will indicate new Procedures that Clients have added to their Procedure list. +If an invalid command (be it by invalid indexes, prefixes, or inputs), the respective errors will be thrown. -* `VersionedAddressBook#commit()` — Saves the current address book state in its history. -* `VersionedAddressBook#undo()` — Restores the previous address book state from its history. -* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history. +The Sequence Diagram below illustrates the interactions within the `Logic` component for the `addProc` API call.: -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +![AddProcCommand](images/AddProcCommand.png) -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +Step 1: Once the user types in the command, the `LogicManager` will be called to execute it. It will use `AddressBookParser` to parse the user command. -Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state. +Step 2: This results in a new `Parser` (more precisely, an object of one of its subclasses e.g., `AddProcCommandParser`) object being constructed. -![UndoRedoState0](images/UndoRedoState0.png) +Step 3: This will result in a new `Procedure` object (based on the user inputs) and a new `Command` object (specifically `AddProcCommand`) being constructed. -Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state. +Step 4: With this, `LogicManager` will call `AddProcCommand` to execute. -![UndoRedoState1](images/UndoRedoState1.png) +Step 5: Within `AddProcCommand`, it will retrieve the `Client` that needs to be added a new `Procedure` and add the new `Procedure` into its Procedure list. -Step 3. The user executes `add n/David …​` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`. +Step 6: Once the `Client` has been updated to include the new `Procedure`, it will update `ModelManager` with the updated `Client` to reflect this change. -![UndoRedoState2](images/UndoRedoState2.png) +### Delete Procedures from a Client (DeleteProc) -
:information_source: **Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`. +The deleteProc mechanism is facilitated by the `DeleteProcCommandParser` and the `DeleteProcCommand`. +The deleteProc mechanism allows deletion of a `Procedure` from an existing `Client` in the address book through interaction with `Storage`. +The specified `Procedure` is permanently deleted. -
+The operation is exposed in the `Model` interface as `Model#setClient(clientToEdit, editedClient)`. -Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state. +The Sequence Diagram below illustrates the interactions within the `Logic` component for `deleteProc`: -![UndoRedoState3](images/UndoRedoState3.png) +![DeleteProcSequenceDiagram](images/DeleteProcSequenceDiagram.png) -
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather -than attempting to perform the undo. +Given below is an example usage scenario and how the `deleteProc` mechanism behaves at each step. -
+Step 1: The UI loads when the application starts. The user finds the Procedure to delete. -The following sequence diagram shows how the undo operation works: +Step 2: The user then calls `deleteProc 1 1` to delete the first `Procedure` in the Procedure Panel that belongs to the first `Client` in the Client Panel. +Then, the `LogicManager` will be called to execute the command, and the input is parsed by the `AddressBookParser`. -![UndoSequenceDiagram](images/UndoSequenceDiagram.png) +Step 3: This results in the creation of a new `Parser`, specifically the `DeleteProcCommandParser`. This parser process the input +and returns an instance of a `DeleteProcCommand`. -
:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. +Step 4: With this, the `LogicManager` will call the `DeleteProcCommand` to execute. -
+Step 5: Within the execution of the `DeleteProcCommand`, it will retrieve the `Client` that contains the `Procedure` to be deleted. +Following, it deletes the specific `Procedure` from the list of procedures that belongs to the `Client`, and returns an edited list of `Procedure`. -The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state. +Step 6: Next, the `procedures` attribute of the `Client` is replaced by the newly-edited list of `Procedure`. This edited list does not contain the +deleted `Procedure`. A copy of the edited `Client` is returned. -
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo. +Step 7: This newly-edited `Client` is then saved locally and stored through the `Model#setClient(Client target, Client editedClient)`. +The `ModelManager` is then eventually updated with the deletion of the `Procedure` and this change is reflected in the Procedure Panel. -
+#### Design considerations: + +**Aspect: Will `deleteProc` permanently delete the `Procedure`?** + +* **Alternative 1 (current choice):** Deletes the entire Procedure. + * Pros: Easy to implement and uses less storage. + * Cons: Users might find it hard to retrieve pre-existing data of the user. + +* **Alternative 2:** Create a deleted status for the `Procedure` and only allow vision of undeleted `Procedure` in the Procedure Panel. + * Pros: User could easily retrieve deleted data. + * Cons: Can get storage-expensive, which makes future operations slower. + +### Edit a Procedure from a Client (EditProc) + +The editProc mechanism is facilitated by the `EditProcCommandParser` and the `EditProcCommand`. +The editProc mechanism allows editing of a `Procedure` from an existing `Client` in the address book. +The existing data of the Procedure is permanently overwritten and the new `Procedure` will be stored locally after. +The specified `Procedure` is permanently overwritten. + +* The `editClientProcedure(Client clientToEdit)` operation is exposed in the `Model` interface as `Model#setClient(Client target, Client editedClient)`. + +The Sequence Diagram below illustrates the interactions within the `Logic` component for `editProc`: + +![EditProcSequenceDiagram](images/EditProcSequenceDiagram.png) + +Given below is an example usage scenario and how the `editProc` mechanism behaves at each step. + +Step 1: UI loads when the application starts. The user finds the Procedure to edit. + +Step 2: The user then calls `editProc 1 1 c/25` to edit the `Cost` of the first `Procedure` in the Procedure Panel that belongs to the first `Client` in the Client Panel. +Then, the `LogicManager` will be called to execute the command, which will be parsed by the `AddressBookParser`. + +Step 3: This results in the creation of a new `Parser`, specifically the `EditProcCommandParser`. This parser processes the input +and returns an instance of an `EditProcCommand`. + +Step 4: With this, the `LogicManager` will call the `EditProcCommand` to execute. + +Step 5: Within the execution of the `EditProcCommand`, it will retrieve the `Client` that contains the `Procedure` to be edited. +Following, it creates a new `Procedure` with the `Cost` attribute edited and the other attributes remaining the same. + +Step 6: Next, the `Procedure` at the specified procedure index in the client's `procedures` attribute is replaced by the newly-created `Procedure`. +A copy of the edited `Client` is returned. + +Step 7: This newly-edited `Client` is saved locally and stored through the `Model#setClient`. +The `ModelManager` is then eventually updated with the edited `Procedure` and this change is reflected in the Procedure Panel. + +#### Design considerations: + +**Aspect: Will `editProc` permanently overwrite pre-existing data?** + +* **Alternative 1 (current choice):** Complete overwrite the pre-existing data. + * Pros: Easy to implement and uses less storage. + * Cons: Users might find it hard to retrieve pre-existing data of the user. + +* **Alternative 2:** Create a new model that saves important information of the pre-existing data. + * Pros: User could easily retrieve previous overwritten data. + * Cons: Can get storage-expensive, which makes future parsing slower. + +### List Procedures By Client (ListProc) + +Lists the Procedures for the given input index of a Client. + +If the Client doesn't have any Procedures, it prints out a different message indicating that. Otherwise, it will simply +print out the success message on result window and update the right column of the UI. + +Below is the sequence diagram for executing ListProcCommand as a user. +![ListProcCommand Sequence Diagram](images/ListProcCommandSequenceDiagram.png) + +Step 1: UI starts when the application starts. + +Step 2: User calls the "listProc 1" command -Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged. +Step 3: LogicManager handles the command from user -![UndoRedoState4](images/UndoRedoState4.png) +Step 4: ModelManager updates the Procedure list accordingly and returns to LogicManager -Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …​` command. This is the behavior that most modern desktop applications follow. +Step 5: UI takes the return value from LogicManager and updates the UI -![UndoRedoState5](images/UndoRedoState5.png) +**Design Considerations** -The following activity diagram summarizes what happens when a user executes a new command: +With other commands like find, an explicit UI update was not necessary based on the AB-3 codebase. +However, even when updating the Procedure list correctly didn't trigger the UI update, which was a serious problem. +The updateFilteredClients method only applies filters to an already existing set of Clients, but +Unlike the list of Clients, Procedures will change drastically from Client to Client. Therefore, +the solution that was to update the actual Procedure list first and then call updateFilteredProcedures to +update the Procedure list; this successfully solved the problem. - +> :question: Why is listProc command is called in the UI before any user input? +> +> :bulb: This displays the first Client's Procedures so that the user can understand +> what the right column is for. + +### List Procedures By Date (ListProcOn) + +#### Implementation + +`ListProcOn` command takes in a date (in the form of dd/mm/yyyy) and returns the Procedures and the Clients' information attached to each of the Procedure that a technician should carry out on that requested date.

+Like other Commands, user input is first parsed by `AddressBookParser`, which is then directed to `ListProcOnCommandParser`, parsing the date into `DateWithoutTime` class, and finally passing down to `ListProcOnCommand` for execution. +Description of `DateWithoutTime` shall be omitted here since the class name is self-explanatory. The below simplified class diagram illustrates such dependencies.
+ + + +`ListProcOnCommand` will then be executed, returning `CommandResult` that can be displayed to the users. The following sequence diagram illustrates the execution of `ListProcOnCommand`. + + + +In short, the command will ask for all the Clients list from the `Model`, and each Client is responsible for returning Procedures that is scheduled on `targetDate`. +`ListProcOnCommand` adds them up together and returns `CommandResult` for subsequent UI operations. #### Design considerations: -**Aspect: How undo & redo executes:** +**Aspect: How to connect each Procedure to its associated Client:**
+This aspect needs to be considered since it is not sufficient just to display all the Procedures for a given date; the user must know about the Procedure's associated Client as well for the feature to be useful. + +* **Alternative 1 (current choice):** Uses a `Pair` container to contain both Procedure and its associated Client when aggregating. + * Pros: No need to create an additional attribute for Procedure connecting to its associated Client. + * Cons: May have performance issues in terms of memory usage, as `ListProcOnCommand` needs to save both the Procedure and its associated Client. + +* **Alternative 2:** Attach an attribute to a Procedure that points to its associated Client class. + * Pros: Easy to implement. (Simply an additional line of code) + * Cons: Cost-related issues (e.g. time consumed for additional integration tests between Procedure and Client, as well as changes to existing tests and sample data) + +### Calculate Cost By Date (Calculate) + +It gives functionality to the cost attribute within the Procedure class by calculating total `Cost` from all Procedures on a specified date. +This provides an instance of total `Cost`, which is not stored locally. +It implements the following operation: + +`Networkers#calculateCost(String date)` — returns total `Cost` from all Procedures on a specified date. + +The operation is exposed in the `Model` interface as `Model#calculateCost()`. + +Given below is an example usage scenario and how the calculateCost feature behaves at each step. + +Step 1. The user would already have Procedures attributed to different Clients in `Networkers` +and would want to calculate all the costs of Procedures conducted today (22/03/2022) + +![CalculateCostObjectDiagram](images/CalculateCostState1.png) + +Step 2. The user executes `calculate 22/03/2022` to calculate cost of all Procedures on `22/03/2022`. + +The following sequence diagram shows how this operation works: + +Firstly, the users input is parsed by the AddressBookParser and the CalculateCommandParser +and a CalculateCommand with the specified date is returned to the logic manager. + +![CalculateSequenceDiagram](images/calculatepart1.png) + +Next, the logicManager, calls the calculateCommand's execute() method. +For each client in the lastshownlist, if the client contains procedures on the specified date, +it will be added to the totalCost variable. + +Finally, the totalCost variable is passed to command result in order to return +the appropriate output. + +![CalculateSequenceDiagram](images/calculatepart2.png) -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. +:information_source: **Note:** The lifeline for `CalculateCommandParser` and `CalculateCommand` should end at the destroy marker (X) +but due to a limitation of PlantUML, the lifeline reaches the end of diagram. -* **Alternative 2:** Individual command knows how to undo/redo by - itself. - * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted). - * Cons: We must ensure that the implementation of each individual command are correct. +1. The arguments passed to the logic manager will be parsed by the AddressBookParser class. +2. If the given arguments are valid, further parsing will be carried out by the CalculateCommandParser. +3. If further parsing is successful, a new CalculateCommand object will be returned -_{more aspects and alternatives to be added}_ +A valid argument consists of 2 sections: +1. valid command `calculate` +2. valid date, `22/03/2022` -### \[Proposed\] Data archiving -_{Explain here how the data archiving feature will be implemented}_ +A date is only valid if it satisfies the isValidDate() method defined in the DateWithoutTime Class -------------------------------------------------------------------------------------------------------------------- @@ -257,71 +434,287 @@ _{Explain here how the data archiving feature will be implemented}_ **Target user profile**: -* has a need to manage a significant number of contacts -* prefer desktop apps over other types -* can type fast -* prefers typing to mouse interactions -* is reasonably comfortable using CLI apps +* Has a need to manage a significant number of businesses (Client(s)) +* Has a need to manage different network-related Procedures for his/her Client(s), such as maintaining and repairing network components like routers and modems +* Prefers desktop apps over other types +* Can type fast +* Prefers typing to mouse interactions +* Is reasonably comfortable using CLI apps -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +**Value proposition**: +* Manage Clients and the respective Procedures faster than a typical mouse/GUI driven app +* Keep important information regarding the user’s business in one platform to manage Clients and past, current, and future Procedures more easily - -### User stories +### Noteworthy user stories Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` -| Priority | As a …​ | I want to …​ | So that I can…​ | -| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- | -| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App | -| `* * *` | user | add a new person | | -| `* * *` | user | delete a person | remove entries that I no longer need | -| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list | -| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident | -| `*` | user with many persons in the address book | sort persons by name | locate a person easily | - -*{More to be added}* +| Priority | As a ... | I want to ... | So that I can ... | +|----------|-----------------------------------------------------------------|-------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------| +| `* * *` | user | add a Client | add a Client to my networkers contact book. | +| `* * *` | user | delete a Client | delete an existing Client when the Client no longer engages with the company. | +| `* * *` | user | edit a Client | edit an existing Client in the networkers contact book. | +| `* * *` | user | add a Procedure to a Client | add a Procedure associated with the Client. | +| `* * *` | user | delete a Procedure from an existing Client | delete a Procedure associated with an existing Client. | +| `* * *` | user | edit a Procedure from an existing Client | edit a Procedure associated with an existing Client. | +| `* * *` | user with many Clients in the address book | view all of my Client(s’) contact information | have a brief idea about how many Client(s) I have at the moment. | +| `* * *` | user with many Clients and Procedures to remember | view all of Procedures related to a specified Client | view all the Procedures that I have done conducted for this Client and potentially use it for events like audit. | +| `* * *` | user with Clients that have details that are constantly in flux | edit the details of a specified Client | ensure that my details of my Clients are up to date | +| `* * ` | user with many Procedures to perform | view all of my Procedures scheduled on a specified day | have a brief idea of which Procedures to perform at a given day. | +| `* * ` | user making expense reports | calculate the total cost of all Procedures performed on a specified day | make an expense report to the company. | ### Use cases -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +(For all use cases below, the System is the Networkers and the Actor is the User, unless specified otherwise) -**Use case: Delete a person** +**Use case 1: Add a Client** **MSS** -1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person +1. User requests to list Client(s). (UC7) +2. User requests to add a Client to the list by specifying its name, number, address, subscription plan, and tag. +3. Networkers adds the Client. + + Use case ends. - Use case ends. +**Extensions** + +* 2a. The name, number, address, or plan is empty. + * 2a1. Networkers shows an error message. + + Use case resumes at step 2. + +* 2b. There is a Client that already has the same address as the one user is trying to add. + * 2b1. Networkers shows an error message. + + Use case resumes at step 2. + +**Use case 2: Delete a Client** + +**MSS** + +1. User requests to list Client(s). (UC7) +2. User sends in a command to delete the Client from the list. +3. Networkers deletes existing Client. + + Use case ends. **Extensions** -* 2a. The list is empty. +* 2a. The user requests to delete a Client out of index. - Use case ends. + * 2a1. Networkers shows an error message. -* 3a. The given index is invalid. + Use case resumes at step 2. - * 3a1. AddressBook shows an error message. +**Use case 3: Edit the details of a Client** + +**MSS** + +1. User requests to list all Client(s). (UC7) +2. User sends in a command that requests to edit the details of a Client at a specified index. +3. Networkers returns a success message as well as an updated Client within the Client list. + + Use case ends. + +**Extensions** + +* 2a. The specified Client is out of index. + * 2a1. Networkers shows an error message. Use case resumes at step 2. -*{More to be added}* +* 2b. The field(s) specified for edit is invalid. + * 2b1. Networkers shows an error message. + + Use case resumes at step 2. + +* 2c. The edited address tag is a duplicate of another Client's address. + * 2c1. Networkers shows an error message. + + Use case resumes at step 2. + +* 2d. The Client's edit parameters are invalid. + * 2d1. Networkers shows an error message. + + Use case resumes at step 2. + + +**Use case 4: Add a Procedure to a Client** + +**MSS** + +1. User requests to list Client(s). (UC7) +2. User requests to add a Procedure by specifying its related information, cost, and date and time, to a specified Client in the list. +3. Networkers adds the Procedure associated with the Client. + + Use case ends. + +**Extensions** + +* 2a. The User requests to add a Procedure to a Client out of index. + * 2a1. Networkers shows an error message. + + Use case resumes at step 2. + +* 2b. The related information, cost, or date and time is empty. + * 2b1. Networkers shows an error message. + + Use case resumes at step 2. + +* 2c. The specified Client already has an identical Procedure in its Procedure list. + * 2c1. Networkers shows an error message. + + Use case resumes at step 2. + +**Use case 5: Delete a Procedure from a Client** + +**MSS** + +1. User requests to list Client(s). (UC7) +2. User sends in a command to delete a specified Procedure from a specified Client in the list. +3. Networkers deletes the Procedure from the Client. + + Use case ends. + +**Extensions** + +* 2a. The specified Procedure is out of index. + * 2a1. Networkers shows an error message. + + Use case resumes at step 2. + +* 2b. The specified Client is out of index. + * 2b1. Networkers shows an error message. + + Use case resumes at step 2. + +* 2c. The User requests to delete a non-existing Procedure from an existing Client. + * 2c1. Networkers shows an error message. + + Use case resumes at step 2. + +* 2d. The User requests to delete an existing Procedure from a non-existing Client. + * 2d1. Networkers shows an error message. + + Use case resumes at step 2. + +**Use case 6: Edit a Procedure from a Client** + +**MSS** + +1. User requests to list Client(s). (UC7) +2. User sends in a command to edit a specified Procedure from a specified Client in the list. +3. Networkers edits the Procedure that belongs to the Client. + + Use case ends. + +**Extensions** + +* 2a. The specified Procedure is out of index. + * 2a1. Networkers shows an error message. + + Use case resumes at step 2. + +* 2b. The specified Client is out of index. + * 2b1. Networkers shows an error message. + + Use case resumes at step 2. + +* 2c. The Client's edit information, cost, and date and time are empty. + * 2c1. Networkers shows an error message. + + Use case resumes at step 2. + +* 2d. The specified Client already has an identical Procedure in its Procedure list. + * 2d1. Networkers shows an error message. + + Use case resumes at step 2. + +**Use case 7: List Client(s) in Networkers** + +**MSS** + +1. User requests to list Client(s). +2. Networkers displays the list of Client(s). + + Use case ends. + +**Use case 8: Listing Procedure(s) of a specified Client** + +**MSS** + +1. User requests to list all Client(s). (UC7) +2. User requests to list Procedure(s) of a specified Client. +3. Networkers displays the list of Procedures in the Client(s). + + Use case ends. + +**Extensions** + +* 2a. The list of Procedures is empty + * 2a1. Networkers shows a message to indicate empty Procedure list. + + Use case ends. + +**Use case 9: Listing all the Procedures that are on a specified day.** + +**MSS** + +1. User sends in a command to list all the Procedures with a specified date. +2. Networkers displays the Procedures along with the location of the Client site. + + Use case ends. + +**Extensions** + +* 1a. The date is invalid. + * 1a1. Networkers shows an error message. + + Use case ends. + +**Use case 10: Calculating the cost of all procedures on a specified day** + +**MSS** + +1. User sends in a command to sum the cost of all Procedures on a specified date. +2. Networkers returns the total cost of all associated Procedures. + + Use case ends. + +**Extensions** + +* 1a. The date is invalid + * 1a1. Networkers shows an error message. + + Use case ends. + +* 1b. There are no Procedures on the specified date. + * 1b1. Networkers returns a cost of $0. + + Use case ends. ### Non-Functional Requirements +#### Technical Requirements +1. Networkers should work on any mainstream OS as long as it has Java 11 or above installed. +2. Networkers should be able to work on both 32-bit and 64-bit environments. +3. Networkers should be backwards compatible. -1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. -2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. -3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +#### Performance Requirements +1. Networkers should respond within two seconds. +2. Should be able to hold up to 1000 Client(s) without a noticeable sluggishness in performance for typical usage. +3. Should be able to hold up to 50 Procedure in each Client without a noticeable sluggishness in performance for typical usage. -*{More to be added}* +#### Quality Requirements +1. A User with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +2. Networkers is only available in English. +3. Networkers is colour-blind friendly. ### Glossary * **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others +* **Client**: Refers to a business entity that the User is responsible for network-related Procedures +* **Procedure**: Refers to a network-related task that a User performs for a Client, such as fixing a router and setting up intranet. -------------------------------------------------------------------------------------------------------------------- @@ -338,40 +731,50 @@ testers are expected to do more *exploratory* testing. 1. Initial launch - 1. Download the jar file and copy into an empty folder + 1. Download the jar file and copy into an empty folder - 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. + 1. Double-click the jar file + Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. 1. Saving window preferences - 1. Resize the window to an optimum size. Move the window to a different location. Close the window. + 1. Resize the window to an optimum size. Move the window to a different location. Close the window. - 1. Re-launch the app by double-clicking the jar file.
+ 1. Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained. -1. _{ more test cases …​ }_ - -### Deleting a person - -1. Deleting a person while all persons are being shown +1. Checking initially displayed Procedures + + 1. Check if the procedures are from the first Client.
+ Expected: The displayed Procedures should all be from client at index 1. - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. +### Deleting a Client - 1. Test case: `delete 1`
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. +1. Deleting a Client while all Clients are being shown - 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. + 1. Prerequisites: List all Clients using the `list` command. Multiple Clients in the list. - 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous. + 1. Test case: `deleteClient 1`
+ Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. -1. _{ more test cases …​ }_ + 1. Test case: `deleteClient 0`
+ Expected: No Client is deleted. Error details shown in the status message. Status bar remains the same. + 1. Other incorrect delete commands to try: `deleteClient`, `deleteClient x`, `...` (where x is larger than the list size)
+ Expected: Similar to previous. + ### Saving data -1. Dealing with missing/corrupted data files +1. Checking if data saves and loads correctly - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ + 1. Launch application. + 2. Delete a Client and close the window. + 3. Launch application again and check if the Client is deleted
+ Expected: the deleted Client is not in the list anymore + +1. Dealing with missing/corrupted data files -1. _{ more test cases …​ }_ + 1. Delete the networkers.json in data folder and launch the application
+ Expected: the networkers.json should be created on launch + 2. To simulate a corrupted data file, change the value of a field of a Client or a Procedure to a non-String value. Then, launch the application.
+ Expected: The networkers.json opens but does not load any Clients and Procedures with an error message in the log. diff --git a/docs/SettingUp.md b/docs/SettingUp.md index 275445bd551..4157083efce 100644 --- a/docs/SettingUp.md +++ b/docs/SettingUp.md @@ -45,7 +45,7 @@ If you plan to use Intellij IDEA (highly recommended): 1. **Learn the design** - When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [AddressBook’s architecture](DeveloperGuide.md#architecture). + When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [Networkers' architecture](DeveloperGuide.md#architecture). 1. **Do the tutorials** These tutorials will help you get acquainted with the codebase. diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3716f3ca8a4..98bb3100eca 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -2,191 +2,458 @@ layout: page title: User Guide --- - -AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps. - * Table of Contents {:toc} - -------------------------------------------------------------------------------------------------------------------- +*Networkers* is a **desktop app for managing contacts for network technicians, +optimised for use via a Command Line Interface** (CLI) +while still having the benefits of a Graphical User Interface (GUI). +If you can type fast, Networkers can get your contact management tasks +done faster than traditional GUI apps. + +-------------------------------------------------------------------------------------------------------------------- ## Quick start -1. Ensure you have Java `11` or above installed in your Computer. +1. Ensure you have Java `11` or above installed on your computer. -1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +2. Download the latest `networkers.jar` from [here](https://github.com/AY2122S2-CS2103T-W13-1/tp/releases). -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +1. Copy the file to the folder you want to use as the _home folder_ for your Networkers. -1. Double-click the file to start the app. The GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
+1. Double-click the file to start the app. The GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
![Ui](images/Ui.png) -1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
+1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
Some example commands you can try: - * **`list`** : Lists all contacts. + * **`list`** : Lists all clients. - * **`add`**`n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book. + * **`addClient`**`n/Apple p/91234561 e/apple@example.com a/311, Bedok Ave 3, #01-15 l/Plan 50GBps t/corporate` : + Adds a Client named `Apple` to the Networkers. + + * **`deleteClient`**`3` : Deletes the 3rd Client shown in the current list. - * **`delete`**`3` : Deletes the 3rd contact shown in the current list. + * **`deleteProc`** `1 3` : Deletes the 3rd Procedure that belongs to the 1st Client in the current list. - * **`clear`** : Deletes all contacts. + * **`editProc`** `1 2 c/67.25` : + Edits the Cost of the 2nd Procedure that belongs to the 1st Client to $67.25. - * **`exit`** : Exits the app. + * **`listProc`**`1` : + Lists the Procedures associated with the 1st Client shown in the client list. -1. Refer to the [Features](#features) below for details of each command. + * **`mark`**`1 1` : Marks the first Procedure in the first Client as complete. + * **`clear`** : Deletes all Clients. + + * **`exit`** : Exits the app. + -------------------------------------------------------------------------------------------------------------------- +## UI Guide + +![UI Guide](images/UIGuide.png) + +### Using the UI + +To use our program, you need to type your commands into the command box as shown in the above image. +After typing, press Enter: if you enter a command with correct format and with valid inputs, +it will execute the command, or else it will guide you towards rectifying the error. + +> :bulb: **Tip:** The Procedures that belong to your first Client in the Client panel will display +> on the Procedure panel upon loading the program! ## Features +The Features section will be generally further divided into the Format, Notes, and Examples subsections for each of the commands. +- The Format subsection discusses how the command should be structured when you type it on Command box. +- The Note subsection is an optional subsection that contains additional information about the command. It may not be related to the format of the command. +- The Example subsection is an optional subsection that illustrates how Networkers responds during and after executing the example commands. -
+### Notes about command formats: +- Words in `UPPER_CASE` refers to the parameters to be supplied by you. + For example, in `addClient n/NAME`, `NAME` is a parameter that can be used as `addClient n/John Doe`. -**:information_source: Notes about the command format:**
+- When more than 1 parameter from the user is required, parameters will be separated with the use of + flags, in the form of `x/xxxxx` + For example, in the add command, since more than one parameter needs to be specified, the command takes + the form of `n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS l/PLAN [t/TAG]`. -* Words in `UPPER_CASE` are the parameters to be supplied by the user.
- e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`. +- Items in square brackets are optional. + For example, `find KEYWORD [MORE_KEYWORDS]` can be used as `find Apple Inc`. -* Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. +- Items with … after them can be used multiple times in a command, including zero times. + e.g. [t/TAG]… can be used as `t/` (i.e. 0 times), `t/new`, or `t/maintenance t/periodic` etc. -* Items with `…`​ after them can be used multiple times including zero times.
- e.g. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc. +- Parameters can be in any order. + e.g. if the command specifies n/NAME p/PHONE_NUMBER, p/PHONE_NUMBER n/NAME is also acceptable. -* Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. +- If a parameter is expected only once in the command but you specified it multiple times, only the last occurrence of the parameter will be taken. + e.g. if you specify p/12341234 p/56785678, only p/56785678 will be taken. + +- All indexes are integer-based, in which the maximum value is 2147483647 (231 - 1), as specified by the Java language. -* If a parameter is expected only once in the command but you specified it multiple times, only the last occurrence of the parameter will be taken.
- e.g. if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken. -* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
- e.g. if the command specifies `help 123`, it will be interpreted as `help`. +### Add a Client: `addClient` -
+Add your Client to Networkers. The Client will initially start off with an empty Procedure list. + +**Format:** `addClient n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS l/PLAN [t/TAG]...` +- `addClient` refers to the command of adding a Client. + +- This command will not work if there exists another Client in your Networkers contact list with an identical, inclusive of whitespaces, ADDRESS attribute. + - Having two whitespaces will be treated differently from having one whitespace. +- There exist some fields that are mandatory for this function. These fields include their name, phone number, email, address and a (subscription) plan. -### Viewing help : `help` +**Note:**
+- You can add any information for the plan attribute. This behaviour is intentional due to differences in formats adopted by different telecommunication companies. -Shows a message explaning how to access the help page. +> :bulb: **Tip:** Our User Guide uses PLAN NAME + BANDWIDTH for inserting plan attributes, such as Plan 50GBps. Feel free to customise to your own needs! -![help message](images/helpMessage.png) +**Example:**
+In Command box: +- `addClient n/Apple p/91234561 e/apple@example.com a/311, Bedok Ave 3, #01-15 l/Plan 50GBps t/corporate` + - This triggers the addition of the Client to your Client Panel. + - Result shows: `New client added: Apple; Phone: 91234561; Email: apple@example.com; Address: 311, Bedok Ave 3, #01-15; Plan: Plan 50GBps; Tags: [corporate]` -Format: `help` +In Application: ![Ui](images/addClientGUI.png) +### Delete a Client: `deleteClient` -### Adding a person: `add` +Deletes a specified Client in Networkers. + +**Format:** `deleteClient ` +- `deleteClient` refers to the command of deleting an existing Client at the index specified in your Client Panel. +- `` refers to the ordering number shown in your displayed Client Panel. + - The index **must be** a positive integer 1, 2, 3, … -Adds a person to the address book. +**Example:**
+In Command box: +- `deleteClient 5` + - This triggers the deletion of the first Client in your Client Panel. + - ❗ When you delete a Client, the Procedures that are tagged to them will be deleted as well. + - Result shows: `Deleted Client: Apple; Phone: 91234561; Email: apple@example.com; Address: 311, Bedok Ave 3, #01-15; Plan: Plan 50GBps; Tags: [corporate]` -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +In Application: ![list](images/deleteClientGUI.png) -
:bulb: **Tip:** -A person can have any number of tags (including 0) -
+### Edit a Client: `edit` -Examples: -* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` -* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal` +Edits a specified Client in Networkers. -### Listing all persons : `list` +**Format:** `edit [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [l/PLAN] [t/TAG]...` +- `edit` refers to the command of editing the detail of an existing Client at the index specified in your Client Panel. +- `` refers to the index number shown in the displayed Client Panel. + - The index **must be** a positive integer 1, 2, 3, … +- `[n/NAME]` refers to an optional field for editing your Client's name. +- `[p/PHONE]` refers to an optional field for editing your Client's contact number. +- `[e/EMAIL]` refers to an optional field for editing your Client's email. +- `[a/ADDRESS]` refers to an optional field for editing your Client's address. +- `[l/PLAN]` refers to an optional field for editing your Client's subscription plan. +- `[t/TAG]` refers to an optional field for editing your Client's tag. + - You can remove all the person’s tags by typing t/ without specifying any tags after it. +- In order to trigger this command, at least one of the following fields must be edited: `name`, `phone`, `email`, +`address`, `plan`, `tag`. +- This command will not allow for edit if there exists another Client in your Networkers contact list with an identical, inclusive of whitespaces, ADDRESS attribute. + - Having two whitespaces will be treated differently from having one whitespace. -Shows a list of all persons in the address book. +- ❗ Note that by editing your Client's details, you will be overwriting their existing data. + +**Example:**
+In Command box: +- `edit 4 n/Apple` + - This triggers the editing of the indicated Client. + - Result shows: `Edited Client: Apple; Phone: 66595327; Email: optical88@example.com; Address: 3155 Commonwealth Ave W, #05-27; Plan: EXPRESS 200MBps; Tags: [new]; Procedures: Information: Extend international warranty of modem; Date: 06/06/2022 12:25; Cost: $195.00; Completed: falseInformation: Back up their employee data to the cloud database; Date: 20/06/2022 09:55; Cost: $230.00; Completed: falseInformation: Inspect the network performance of their cash register; Date: 23/08/2022 13:15; Cost: $50.00; Completed: false` + +In Application: ![Ui](images/EditClient_After.png) + +### Add a Procedure to a Client: `addProc` + +Adds a specified Procedure to a specified Client in your displayed Client Panel. -Format: `list` +**Format:** `addProc i/INFORMATION c/COST d/DATE_TIME` +- `addProc` refers to the command of adding a Procedure to the Client at the specified index. +- `` refers to the index number shown in the displayed Client Panel. + - The index **must be** a positive integer 1, 2, 3, … +- `INFORMATION` refers to the tasks or problems that need to be addressed on the Client’s business site. + Blank input for this field will not be valid. +- `DATE_TIME` is the date and time that the Procedure takes place. It accepts inputs in the form of `dd/MM/YYYY HH:MM`, e.g. 20/03/2022 11:30. +- `COST` refers to the cost of Procedure that will be charged to your Client. + It must be greater than $0 and less than $100 million dollars. + The rationale behind a cap for cost would be to prevent technicians from inputting numbers that are too large by accident. + Contracts that technicians handle on a day-to-day basis are generally small scale and would be less than $100 million. + +- If the specified Client already has an identical Procedure, the application will inform you that the Procedure has already been added. + - An identical Procedure refers to a Procedure that contains the exact same information, date and time, and cost. -### Editing a person : `edit` +**Note:**
+- If the Procedure Panel was showing the Procedures of another Client prior to executing this command, +it will now show the Procedures of the Client that has a Procedure newly added. +- New Procedures will be auto-sorted, in ascending order, based on the date the Procedure will take place. +- It is valid to add Procedures that fall on the exact same date and time. + - This is because some Procedures can be done concurrently through remote control. +- It is valid to add in Procedures that had occurred before. + - This is because some technicians have mentioned that they need to add in past Procedures just for their own record. -Edits an existing person in the address book. +**Example:**
+In Command box: +- `addProc 1 i/Install modem c/10.5 d/20/03/2022 11:30` + - This triggers the addition of a Procedure to the first Client. + - Result shows: `New Procedure added: Information: Install modem; Date: 20/03/2022 11:30; Cost: $10.50; Completed: false` -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +In Application: ![addProc](images/addProcGUI.png) + +### Delete a Procedure from the Client: `deleteProc` + +Deletes a Procedure associated with your Client. This is important as it allows you to maintain and make changes to the database. + +**Format:** `deleteProc ` +- `deleteProc` refers to the command of deleting a Procedure from the Client at the specified index. +- `` refers to the index number shown in the displayed Client Panel. + - The index **must be** a positive integer 1, 2, 3, ... +- `` refers to the index number shown in the displayed Procedure Panel. + - The index **must be** a positive integer 1, 2, 3, ... + +**Note:**
+- If the Procedure Panel was showing the Procedures of another Client prior to executing this command, + it will now show the Procedures of the Client that has their Procedure deleted. -* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ -* At least one of the optional fields must be provided. -* Existing values will be updated to the input values. -* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative. -* You can remove all the person’s tags by typing `t/` without - specifying any tags after it. +**Example:**
+In Command box: +- `deleteProc 1 3` + - Result shows: + `Deleted Procedure: Information: Run a network diagnostic test; Date: 05/04/2022 11:55; Cost: $10.00; Completed: true` + +Before Command: + +![deleteProc](images/DeleteProcCommand_Before.png) + +After Command: + +![deleteProc](images/DeleteProcCommand_After.png) + +### Edit a Procedure of your Client: `editProc` + +Edits an existing Procedure that belongs to an existing Client. This feature allows you to edit the main details related to the Procedure. +The main details include the information, the date and time, and the cost of the Procedure. + +**Format:** `editProc [i/INFORMATION] [d/DATE_TIME] [c/COST]` +- `editProc` refers to the command of editing an existing Procedure from an existing Client. +- `` refers to the ordering number of the Client displayed on the Client screen. + - The index **must be** a positive integer 1, 2, 3, ... +- `` refers to the ordering number of the Procedure displayed on the Procedure screen (that is associated with a Client). + - The index **must be** a positive integer 1, 2, 3, ... +- `[i/INFORMATION]` refers to the informational detail of the Procedures in subsequent servicing trips. + Blank input for this field will not be valid. +- `[d/DATE_TIME]` is the date and time that the Procedure takes place. It accepts inputs in the form of `dd/MM/YYYY HH:MM`, e.g. 20/03/2022 11:30. +- `[c/COST]` refers to the cost of Procedure that will be charged to your Client. + It must be greater than $0 and less than $100 million dollars. + The rationale behind a cap for cost would be to prevent technicians from inputting numbers that are too large by accident. + Contracts that technicians handle on a day-to-day basis are generally small scale and would be less than $100 million. + +- The `INFORMATION`, the `DATE_TIME`, or the `COST` field **must be** filled up for this command to run. +- If the Client already has an identical Procedure, the application will inform you that the Client already has this Procedure. + - An identical Procedure refers to a Procedure that has the exact same information, date and time, and cost. -Examples: -* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively. -* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags. +**Note:**
+- After the Procedure is successfully edited, all procedures will be auto-sorted, in ascending order, based on the + date the Procedure will take place. +- If the Procedure Panel was showing the Procedures of another Client prior to executing this command, + it will now show the Procedures of the Client that has their Procedure deleted. -### Locating persons by name: `find` +**Example:**
+In Command box: +- `editProc 1 2 i/Fix Router d/31/03/2022 09:50 c/67.25` + - Result shows: + `Edited Procedure: Information: Fix Router; Date: 31/03/2022 09:50; Cost: $67.25; Completed: true. + From client: MINISO, at 3155 Commonwealth Ave W, #03-56-58.` + +Before Command: + +![editProc](images/EditProcCommand_Before.png) + +After Command: -Finds persons whose names contain any of the given keywords. +![editProc](images/EditProcCommand_After.png) -Format: `find KEYWORD [MORE_KEYWORDS]` +### View All Clients: `list` -* The search is case-insensitive. e.g `hans` will match `Hans` -* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans` -* Only the name is searched. -* Only full words will be matched e.g. `Han` will not match `Hans` -* Persons matching at least one keyword will be returned (i.e. `OR` search). - e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` +Lists out all the Clients saved in your Networkers database. +This feature is commonly used to list all the Clients after executing `find`. +No secondary information is required. -Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +**Format:** `list` +- `list` refers to the command to list all Clients saved in your database. -### Deleting a person : `delete` +**Note:**
+- After using the `find` command, you can use this feature to see all the Clients again. +- If this command is used after the `find` command, the Procedure Panel will feature the + Procedures from the previously found Client (if any). -Deletes the specified person from the address book. +**Example:**
+In Command box: +- `list` + - Result shows: `Listed all clients.` -Format: `delete INDEX` +In Application: ![list](images/listGUI.png) -* Deletes the person at the specified `INDEX`. -* The index refers to the index number shown in the displayed person list. -* The index **must be a positive integer** 1, 2, 3, …​ +### View All Procedures of a Client: `listProc` -Examples: -* `list` followed by `delete 2` deletes the 2nd person in the address book. -* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command. +Lists out all the Procedures related to a Client. -### Clearing all entries : `clear` +**Format:** `listProc ` +- `listProc` refers to the command to list all the Procedures related to the specified, and existing, Client. +- `` refers to the ordering number of the Client displayed on the Client screen. + - The index **must be** a positive integer 1, 2, 3, ... -Clears all entries from the address book. +**Example:**
+In Command box: +- `listProc 3` + - Result shows: `Procedures successfully loaded.` -Format: `clear` +In Application: ![listProc](images/ListProcCommandExample1.png) -### Exiting the program : `exit` +### View all Procedures scheduled on a specified date: `listProcOn` -Exits the program. +Lists out all Procedures, including the associated Client as per Procedure, that are scheduled on a specified date. -Format: `exit` +**Format:** `listProcOn ` +- `listProcOn` refers to the command of listing out all Procedures on a specified date. +- `DATE` is in the format of dd/MM/YYYY, e.g. 26/03/2022. + - An error will be thrown if the date is invalid. -### Saving the data +**Example:**
+In Command box: +- `listProcOn 06/06/2022` + - Result shows: +``` +Listing Procedures on requested date: +1. Information: Back up their employee data to the cloud database; Date: 06/06/2022 09:55; Cost: $230.00; Completed: false +Master Fix Services, located at 3155 Commonwealth Ave W, #B1-10 +2. Information: Back up their employee data to the cloud database; Date: 06/06/2022 09:55; Cost: $230.00; Completed: false +Mr Bean, located at 3155 Commonwealth Ave W, #B1-K13 +3. Information: Extend international warranty of modem; Date: 06/06/2022 12:25; Cost: $195.00; Completed: false +Optical 88, located at 3155 Commonwealth Ave W, #05-27 +``` -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +In Application: ![listProcOn](images/listProcOnNew.png) -### Editing the data file +### Calculate the cost of all Procedures on a specified date: `calculate` -AddressBook data are saved as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file. +Calculates the cost of all Procedures that happen at any time on a specified date. -
:exclamation: **Caution:** -If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. -
+**Format:** `calculate ` +- `calculate` refers to the command of calculating the cost of all Procedures on a specified date. +- `DATE` is in the format of dd/MM/YYYY, e.g. 23/03/2022. + - An error will be thrown if the date is invalid. -### Archiving data files `[coming in v2.0]` +**Example:**
+In Command box: +- `calculate 06/06/2022` + - Result shows: `Total Cost: $655.00` -_Details coming soon ..._ +In Application: ![list](images/calculate.png) --------------------------------------------------------------------------------------------------------------------- +### Marking a Procedure of a Client as Completed: `mark` + +Marks the target Client's target Procedure as completed. + +**Format:** `mark ` +- `mark` refers to the command of marking a Procedure as completed. +- `` refers to the ordering number of the Client displayed on the Client screen. + - The index **must be** a positive integer 1, 2, 3, ... +- `` refers to the ordering number of the Procedure displayed on the Procedure screen. + - The index **must be** a positive integer 1, 2, 3, ... + +**Example:** +In Command box: +- `mark 1 5` + - Result shows: `Procedure successfully marked as complete.` + +In Application: ![mark](images/mark.png) + +### Unmarking a Procedure of a Client: `unmark` + +Marks the target Client's target Procedure as not complete. + +**Format:** `unmark ` +- `unmark` refers to the command of marking a Procedure as not completed. +- `` refers to the ordering number of the Client displayed on the Client screen. + - The index **must be** a positive integer 1, 2, 3, ... +- `` refers to the ordering number of the Procedure displayed on the Procedure screen. + - The index **must be** a positive integer 1, 2, 3, ... + +**Example:** +In Command box: +- `unmark 1 5` + - Result shows: `Procedure successfully unmarked.` + +In Application: ![mark](images/unmark.png) + +### Locating Clients by Name: `find` + +Finds Clients whose names contain any of the given keywords. + +**Format:** `find KEYWORD [MORE_KEYWORDS]` +- `find` refers to the command of finding Clients whose names fit the given keywords. +- The search is case-insensitive. e.g. `apple inc` will match `Apple Inc` +- The order of the keywords does not matter. e.g. `Inc Apple` will match `Apple Inc` +- Only the name is searched. +- Only full words will be matched e.g. `App` will not match `Apple Inc` +- Clients matching at least one keyword will be returned. e.g. `Inc` will return `Apple Inc`, `Google Inc` + +**Example:**
+In Command box: +- `find Fix` + - Result shows: `1 client(s) listed!` +In Application: ![list](images/findCommandExample.png) + +### Clear All Clients: `clear` + +Clears all Clients and their respective Procedures currently recorded in Networkers. + +**Format:** `clear` +- `clear` refers to the command of clearing all Clients and their respective Procedures in the application. + +**Example:**
+In Command box: +- `clear` + - Result shows: `Address book has been cleared!` + +In Application: ![clear](images/ClearCommand_After.png) + +### Exiting the program: `exit` + +Exits the program. + +**Format**: `exit` + +## Command Summary + +| Command | Syntax | Example | +|-------------------------------------------------|-------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------| +| Add Client | `addClient n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS l/PLAN [t/TAG]...` | `addClient n/Apple p/91234561 e/apple@example.com a/311, Bedok Ave 3, #01-15 l/Plan 50GBps t/corporate` | +| Delete Client | `deleteClient ` | `deleteClient 1` | +| Edit Client | `edit [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [l/PLAN] [t/TAG]...` | `edit 1 n/Apple` | +| Add Procedure | `addProc i/INFORMATION c/COST d/DATE_TIME` | `addProc 1 i/Install modem c/10.5 d/20/03/2022 11:30` | +| Delete Procedure | `deleteProc ` | `deleteProc 1 3` | +| Edit Procedure | `editProc [i/INFORMATION] [d/DATE_TIME] [c/COST]` | `editProc 1 2 i/Fix Router d/31/03/2022 09:50 c/67.25` | +| List All Clients | `list` | `list` | +| List All Procedures of a Client | `listProc ` | `listProc 1` | +| List All Procedures on Specified Date | `listProcOn ` | `listProcOn 23/05/2022` | +| Calculate Cost of Procedures (on specific date) | `calculate ` | `calculate 23/02/2022` | +| Clear All Clients | `clear` | `clear` | +| Find Clients by Keyword | `find KEYWORD [MORE_KEYWORDS]...` | `find Apple Inc` | +| Mark Procedure as complete | `mark ` | `mark 1 1` | +| Unmark completed Procedure | `unmark ` | `unmark 1 1` | +| Exit program | `exit` | `exit` | ## FAQ -**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder. +### Why is The Command Not Working? --------------------------------------------------------------------------------------------------------------------- +There are several cases in which you might face errors when entering a command: + +- Typing in the command word wrongly (e.g. typing `lisst` instead of `list`) +- Typing in wrong number of inputs (e.g. typing `deleteClient` or `deleteClient 5 2` instead of `deleteClient 2`) +- Typing in letters for numbers or vice versa (e.g. typing `listProc hello` instead of `listProc 1`) +- You entered the correct command but with invalid inputs (e.g. typing `deleteProc 1 10` when there is no 10th Client) -## Command summary - -Action | Format, Examples ---------|------------------ -**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague` -**Clear** | `clear` -**Delete** | `delete INDEX`
e.g., `delete 3` -**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…​`
e.g.,`edit 2 n/James Lee e/jameslee@example.com` -**Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` -**List** | `list` -**Help** | `help` +The corresponding error messages will be displayed in the result box, so do take a look at them. diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..fdb082c83ff 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "Networkers" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2122S2-CS2103T-W13-1/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..aac01a8cfd7 100644 --- a/docs/_sass/minima/_base.scss +++ b/docs/_sass/minima/_base.scss @@ -288,7 +288,7 @@ table { text-align: center; } .site-header:before { - content: "AB-3"; + content: "Networkers"; font-size: 32px; } } diff --git a/docs/diagrams/AddProcCommand.puml b/docs/diagrams/AddProcCommand.puml new file mode 100644 index 00000000000..37385cd2b5d --- /dev/null +++ b/docs/diagrams/AddProcCommand.puml @@ -0,0 +1,103 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":AddProcCommandParser" as AddProcCommandParser LOGIC_COLOR +participant "d:AddProcCommand" as AddProcCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "p:Procedure" as Procedure MODEL_COLOR +participant ":ModelManager" as ModelManager MODEL_COLOR +participant ":Client" as Client MODEL_COLOR +end box + + +[-> LogicManager : execute('procedure') +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand('procedure') +note left +('procedure') is defined as +"addProc 1 i/Install modem +c/10.5 d/20/03/2022 11:30" +end note +activate AddressBookParser + +create AddProcCommandParser +AddressBookParser -> AddProcCommandParser : parse(args) +activate AddProcCommandParser +note left +(args) is defined as + i/Install modem + c/10.5 + d/20/03/2022 + 11:30 +end note + + + +create Procedure +AddProcCommandParser -> Procedure +activate Procedure +Procedure --> AddProcCommandParser : p +deactivate Procedure +Procedure -[hidden]-> AddProcCommandParser +destroy Procedure + +create AddProcCommand +AddProcCommandParser -> AddProcCommand +activate AddProcCommand +AddProcCommand --> AddProcCommandParser : d +deactivate AddProcCommand +AddProcCommandParser --> AddressBookParser : d +deactivate AddProcCommandParser + +AddressBookParser --> LogicManager : d +deactivate AddressBookParser + +LogicManager -> AddProcCommand : execute() +activate AddProcCommand + +AddProcCommand -> ModelManager : getFilteredClientList() +activate ModelManager + +ModelManager --> AddProcCommand : lastShownList +deactivate ModelManager + +AddProcCommand -> AddProcCommand : addClientProcedure(clientToEdit) +activate AddProcCommand + + +AddProcCommand -> Client : get(info) +activate Client +note left +info represents +Name, Phone, Email, +Plans, Tags, Address +and Procedures +end note +Client --> AddProcCommand : info +deactivate Client +Client -[hidden]--> AddProcCommand : info +destroy Client + +AddProcCommand -> AddProcCommand : addProcedure(procedureList) +activate AddProcCommand +AddProcCommand --> AddProcCommand +deactivate AddProcCommand +AddProcCommand --> AddProcCommand +deactivate AddProcCommand +AddProcCommand -> ModelManager : setClient(clientToEdit, editedClient) + + +activate ModelManager +AddProcCommand -> ModelManager : updatedFilteredClientList(PREDICATE_SHOW_ALL_CLIENTS) +deactivate ModelManager +AddProcCommand --> LogicManager +deactivate AddProcCommand +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index ef81d18c337..66404cbc778 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -7,15 +7,16 @@ Participant ":Logic" as logic LOGIC_COLOR Participant ":Model" as model MODEL_COLOR Participant ":Storage" as storage STORAGE_COLOR -user -[USER_COLOR]> ui : "delete 1" +user -[USER_COLOR]> ui : "deleteClient 1" activate ui UI_COLOR -ui -[UI_COLOR]> logic : execute("delete 1") +ui -[UI_COLOR]> logic : execute("deleteClient 1") activate logic LOGIC_COLOR -logic -[LOGIC_COLOR]> model : deletePerson(p) -activate model MODEL_COLOR +logic -[LOGIC_COLOR]> model : deleteClient(c) +activate model MODEL_COLOR +note left: c refers to the\n Client at index 1 model -[MODEL_COLOR]-> logic deactivate model diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml index 5731f9cbaa1..2845d6f216e 100644 --- a/docs/diagrams/BetterModelClassDiagram.puml +++ b/docs/diagrams/BetterModelClassDiagram.puml @@ -4,18 +4,21 @@ skinparam arrowThickness 1.1 skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR -AddressBook *-right-> "1" UniquePersonList +AddressBook *-right-> "1" UniqueClientList AddressBook *-right-> "1" UniqueTagList -UniqueTagList -[hidden]down- UniquePersonList -UniqueTagList -[hidden]down- UniquePersonList +UniqueTagList -[hidden]down- UniqueClientList +UniqueTagList -[hidden]down- UniqueClientList UniqueTagList *-right-> "*" Tag -UniquePersonList -right-> Person +UniqueClientList -right-> Client -Person -up-> "*" Tag +Client -up-> "*" Tag -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address +Client *--> Name +Client *--> Phone +Client *--> Email +Client *--> Address +Client *--> Plan +Client *--> "*" Tag +Client *--> "*" Procedure @enduml diff --git a/docs/diagrams/CalculateCostState1.puml b/docs/diagrams/CalculateCostState1.puml new file mode 100644 index 00000000000..b68d5fb6215 --- /dev/null +++ b/docs/diagrams/CalculateCostState1.puml @@ -0,0 +1,31 @@ +@startuml +Object Miniso + +object MinisoProcedure1 +MinisoProcedure1 : info = "replace switches" +MinisoProcedure1 : cost = "150" +MinisoProcedure1 : date = "22/03/2022" +object MinisoProcedure2 +MinisoProcedure2 : info = "replace modem" +MinisoProcedure2 : cost = "130" +MinisoProcedure2 : date = "22/03/2022" + +Miniso *-- MinisoProcedure1 +Miniso *-- MinisoProcedure2 + +object Apple + +object AppleProcedure1 +AppleProcedure1 : info = "fix router" +AppleProcedure1 : cost = "100.50" +AppleProcedure1 : date = "22/03/2022" + + +object AppleProcedure2 +AppleProcedure2 : info = "replace wires" +AppleProcedure2 : cost = "50" +AppleProcedure2 : date = "04/01/2022" + +Apple *-- AppleProcedure1 +Apple *-- AppleProcedure2 +@enduml diff --git a/docs/diagrams/CalculateSequenceDiagram.png b/docs/diagrams/CalculateSequenceDiagram.png new file mode 100644 index 00000000000..508771d479e Binary files /dev/null and b/docs/diagrams/CalculateSequenceDiagram.png differ diff --git a/docs/diagrams/CalculateSequenceDiagram.puml b/docs/diagrams/CalculateSequenceDiagram.puml new file mode 100644 index 00000000000..02f07cd4c7f --- /dev/null +++ b/docs/diagrams/CalculateSequenceDiagram.puml @@ -0,0 +1,92 @@ +@startuml + +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":CalculateCommandParser" as CalculateCommandParser LOGIC_COLOR +participant "totalCost:BigDecimal" as totalCost LOGIC_COLOR +participant "cc:CalculateCommand" as CalculateCommand LOGIC_COLOR +participant "cr:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("calculate 22/03/2022") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("calculate 22/03/2022") +activate AddressBookParser + +create CalculateCommandParser +AddressBookParser -> CalculateCommandParser +activate CalculateCommandParser + +CalculateCommandParser --> AddressBookParser +deactivate CalculateCommandParser + +AddressBookParser -> CalculateCommandParser : parse("22/03/2022") +activate CalculateCommandParser + +create CalculateCommand +CalculateCommandParser -> CalculateCommand +activate CalculateCommand + +CalculateCommand --> CalculateCommandParser : cc +deactivate CalculateCommand + +CalculateCommandParser --> AddressBookParser : cc +deactivate CalculateCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +CalculateCommandParser -[hidden]-> AddressBookParser +destroy CalculateCommandParser + +AddressBookParser --> LogicManager : cc +deactivate AddressBookParser + +LogicManager -> CalculateCommand : execute() +activate CalculateCommand + +CalculateCommand -> Model : getFiltereredClientList() +activate Model + + +Model --> CalculateCommand : lastshownlist +deactivate Model + +Create totalCost +CalculateCommand -> totalCost : BigDecimal() +activate totalCost + +loop number of clients +CalculateCommand -> totalCost : add cost of procedures on \nspecified date for each client + +end +totalCost --> CalculateCommand : totalCost +deactivate totalCost + +'Hidden arrow to position the destroy marker below the end of the activation bar. +totalCost -[hidden]-> CalculateCommand +destroy totalCost + + + +create CommandResult +CalculateCommand -> CommandResult +activate CommandResult + +CommandResult --> CalculateCommand :cr +deactivate CommandResult + +CalculateCommand --> LogicManager : cr +deactivate CalculateCommand +'Hidden arrow to position the destroy marker below the end of the activation bar. +CalculateCommand -[hidden]-> LogicManager +destroy CalculateCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ComponentManagers.puml b/docs/diagrams/ComponentManagers.puml index 5e907dc1115..edc151f9f10 100644 --- a/docs/diagrams/ComponentManagers.puml +++ b/docs/diagrams/ComponentManagers.puml @@ -10,13 +10,13 @@ Class LogicManager } package Model{ -Class "<>\nModel" as Model -Class ModelManager +Class "<>\nModel" as Model MODEL_COLOR +Class ModelManager MODEL_COLOR } package Storage{ -Class "<>\nStorage" as Storage -Class StorageManager +Class "<>\nStorage" as Storage STORAGE_COLOR +Class StorageManager STORAGE_COLOR } Class HiddenOutside #FFFFFF diff --git a/docs/diagrams/DeleteProcCommand.puml b/docs/diagrams/DeleteProcCommand.puml new file mode 100644 index 00000000000..28c8a4d8346 --- /dev/null +++ b/docs/diagrams/DeleteProcCommand.puml @@ -0,0 +1,98 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":DeleteProcCommandParser" as DeleteProcCommandParser LOGIC_COLOR +participant "dpc:DeleteProcCommand" as DeleteProcCommand LOGIC_COLOR +participant "cr:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":ModelManager" as ModelManager MODEL_COLOR +end box + +create LogicManager +-> LogicManager : execute("deleteProc 1 1") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("deleteProc 1 1") +activate AddressBookParser + +create DeleteProcCommandParser +AddressBookParser -> DeleteProcCommandParser +activate DeleteProcCommandParser +DeleteProcCommandParser --> AddressBookParser +deactivate DeleteProcCommandParser + +AddressBookParser -> DeleteProcCommandParser : parse("1 1") +activate DeleteProcCommandParser +create DeleteProcCommand +DeleteProcCommandParser -> DeleteProcCommand +activate DeleteProcCommand + +DeleteProcCommand --> DeleteProcCommandParser : dpc +deactivate DeleteProcCommand + +DeleteProcCommandParser --> AddressBookParser : dpc +deactivate DeleteProcCommandParser + +DeleteProcCommandParser -[hidden]-> AddressBookParser : dpc +destroy DeleteProcCommandParser + +AddressBookParser --> LogicManager : dpc +deactivate AddressBookParser + +LogicManager -> DeleteProcCommand : execute(command) +activate DeleteProcCommand + +DeleteProcCommand -> ModelManager : gFCL() +activate ModelManager + +note left + gFCL() denotes the method getFilteredClientList() +end note + +ModelManager --> DeleteProcCommand +deactivate ModelManager + +DeleteProcCommand -> DeleteProcCommand : deleteProcedureFromList(procInfo) +note left + the procInfo denotes the + Client's Procedures and the + target Procedure index. +end note +activate DeleteProcCommand + +DeleteProcCommand --> DeleteProcCommand +deactivate DeleteProcCommand + + +DeleteProcCommand -> ModelManager : setClient(info) +activate ModelManager + +note left + the info denotes the target, + and the editedClient info. +end note + +ModelManager --> DeleteProcCommand +deactivate ModelManager + +create CommandResult +DeleteProcCommand -> CommandResult +activate CommandResult + +CommandResult --> DeleteProcCommand : cr +deactivate CommandResult + +DeleteProcCommand --> LogicManager : cr +deactivate DeleteProcCommand + +DeleteProcCommand -[hidden]-> LogicManager +destroy DeleteProcCommand + +<-- LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/DeleteProcState0.puml b/docs/diagrams/DeleteProcState0.puml new file mode 100644 index 00000000000..23e7224bda5 --- /dev/null +++ b/docs/diagrams/DeleteProcState0.puml @@ -0,0 +1,20 @@ +@startuml +!include style.puml +skinparam ClassFontColor #000000 +skinparam ClassBorderColor #000000 + +title Initial state + +package States { + class State1 as "__ab0:AddressBook__" + class State2 as "__ab1:AddressBook__" + class State3 as "__ab2:AddressBook__" +} +State1 -[hidden]right-> State2 +State2 -[hidden]right-> State3 +hide State2 +hide State3 + +class Pointer as "Current State" +Pointer -up-> State1 +@end diff --git a/docs/diagrams/DeleteProcState1.puml b/docs/diagrams/DeleteProcState1.puml new file mode 100644 index 00000000000..702dfb4bb5c --- /dev/null +++ b/docs/diagrams/DeleteProcState1.puml @@ -0,0 +1,22 @@ +@startuml +!include style.puml +skinparam ClassFontColor #000000 +skinparam ClassBorderColor #000000 + +title After command "deleteProc 1 1" + +package States <> { + class State1 as "__ab0:AddressBook__" + class State2 as "__ab1:AddressBook__" + class State3 as "__ab2:AddressBook__" +} + +State1 -[hidden]right-> State2 +State2 -[hidden]right-> State3 + +hide State3 + +class Pointer as "Current State" + +Pointer -up-> State2 +@end diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 1dc2311b245..d81cb6d3104 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -4,8 +4,8 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR -participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR -participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR +participant ":DeleteClientCommandParser" as DeleteClientCommandParser LOGIC_COLOR +participant "d:DeleteClientCommand" as DeleteClientCommand LOGIC_COLOR participant ":CommandResult" as CommandResult LOGIC_COLOR end box @@ -13,56 +13,59 @@ box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR end box -[-> LogicManager : execute("delete 1") +[-> LogicManager : execute("deleteClient 1") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") +LogicManager -> AddressBookParser : parseCommand("deleteClient 1") activate AddressBookParser -create DeleteCommandParser -AddressBookParser -> DeleteCommandParser -activate DeleteCommandParser +create DeleteClientCommandParser +AddressBookParser -> DeleteClientCommandParser +activate DeleteClientCommandParser -DeleteCommandParser --> AddressBookParser -deactivate DeleteCommandParser +DeleteClientCommandParser --> AddressBookParser +deactivate DeleteClientCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") -activate DeleteCommandParser +AddressBookParser -> DeleteClientCommandParser : parse("1") +activate DeleteClientCommandParser -create DeleteCommand -DeleteCommandParser -> DeleteCommand -activate DeleteCommand +create DeleteClientCommand +DeleteClientCommandParser -> DeleteClientCommand +activate DeleteClientCommand -DeleteCommand --> DeleteCommandParser : d -deactivate DeleteCommand +DeleteClientCommand --> DeleteClientCommandParser : d +deactivate DeleteClientCommand -DeleteCommandParser --> AddressBookParser : d -deactivate DeleteCommandParser +DeleteClientCommandParser --> AddressBookParser : d +deactivate DeleteClientCommandParser 'Hidden arrow to position the destroy marker below the end of the activation bar. -DeleteCommandParser -[hidden]-> AddressBookParser -destroy DeleteCommandParser +DeleteClientCommandParser -[hidden]-> AddressBookParser +destroy DeleteClientCommandParser AddressBookParser --> LogicManager : d deactivate AddressBookParser -LogicManager -> DeleteCommand : execute() -activate DeleteCommand +LogicManager -> DeleteClientCommand : execute() +activate DeleteClientCommand -DeleteCommand -> Model : deletePerson(1) +DeleteClientCommand -> Model : deleteClient(1) activate Model -Model --> DeleteCommand +Model --> DeleteClientCommand deactivate Model create CommandResult -DeleteCommand -> CommandResult +DeleteClientCommand -> CommandResult activate CommandResult -CommandResult --> DeleteCommand +CommandResult --> DeleteClientCommand deactivate CommandResult -DeleteCommand --> LogicManager : result -deactivate DeleteCommand +DeleteClientCommand --> LogicManager : result +deactivate DeleteClientCommand +'Hidden arrow to position the destroy marker below the end of the activation bar. +DeleteClientCommand -[hidden]-> DeleteClientCommandParser +destroy DeleteClientCommand [<--LogicManager deactivate LogicManager diff --git a/docs/diagrams/EditProcCommand.puml b/docs/diagrams/EditProcCommand.puml new file mode 100644 index 00000000000..f1aaa1f904a --- /dev/null +++ b/docs/diagrams/EditProcCommand.puml @@ -0,0 +1,109 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":EditProcCommandParser" as EditProcCommandParser LOGIC_COLOR +participant "epc:EditProcCommand" as EditProcCommand LOGIC_COLOR +participant "cr:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":ModelManager" as ModelManager MODEL_COLOR +end box + +create LogicManager +-> LogicManager : execute(userInput) +note left + userInput denotes + "editProc 1 1 c/30" +end note + +activate LogicManager +LogicManager -> AddressBookParser : parseCommand("editProc 1 1 ") +activate AddressBookParser + +create EditProcCommandParser +AddressBookParser -> EditProcCommandParser +activate EditProcCommandParser +EditProcCommandParser --> AddressBookParser +deactivate EditProcCommandParser + +AddressBookParser -> EditProcCommandParser : parse("1 1 c/30") +activate EditProcCommandParser + + +note left + new Parser() + denotes new + EditProcCommandParser() +end note + + +create EditProcCommand +EditProcCommandParser -> EditProcCommand + +activate EditProcCommand +EditProcCommand --> EditProcCommandParser : epc +deactivate EditProcCommand + +EditProcCommandParser --> AddressBookParser: epc +deactivate EditProcCommandParser + +AddressBookParser --> LogicManager : epc +deactivate AddressBookParser + +AddressBookParser -[hidden]-> LogicManager +destroy EditProcCommandParser + +LogicManager -> EditProcCommand : execute(command) +activate EditProcCommand + +EditProcCommand -> ModelManager : gFCL() +activate ModelManager + +note left + gFCL() denotes the method: + getFilteredClientList() +end note + +ModelManager --> EditProcCommand +deactivate ModelManager + +EditProcCommand -> EditProcCommand : editClientProcedures(info) +note left + info denotes the target Client, + and the updated Procedures. +end note +activate EditProcCommand + +EditProcCommand --> EditProcCommand +deactivate EditProcCommand + +EditProcCommand -> ModelManager : setClient(targetClient, editedClient) +note left + the info denotes the target, + and the editedClient info. +end note + +activate ModelManager +ModelManager --> EditProcCommand +deactivate ModelManager + +create CommandResult +EditProcCommand -> CommandResult +activate CommandResult + +CommandResult --> EditProcCommand : cr +deactivate CommandResult + +EditProcCommand --> LogicManager: cr +deactivate EditProcCommand + +EditProcCommand -[hidden]-> LogicManager +destroy EditProcCommand + +<-- LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ListProcCommand.puml b/docs/diagrams/ListProcCommand.puml new file mode 100644 index 00000000000..3c1780f55b1 --- /dev/null +++ b/docs/diagrams/ListProcCommand.puml @@ -0,0 +1,71 @@ +@startuml +!include style.puml +!define LOGIC_COLOR_T1 #7777DB + +box UI UI_COLOR_T1 +participant ":MainWindow" as MainWindow UI_COLOR +end box + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant "lpc:ListProcCommand" as ListProcCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":ModelManager" as ModelManager MODEL_COLOR +end box + +-> MainWindow : show() + +activate MainWindow +MainWindow -> MainWindow : executeCommand("listProc 1") + +'LogicManager column' +activate LogicManager +MainWindow -> LogicManager : execute("listProc 1") +LogicManager -> AddressBookParser : parseCommand("listProc 1") +activate AddressBookParser + +create ListProcCommand +AddressBookParser -> ListProcCommand : new + +activate ListProcCommand +AddressBookParser <-- ListProcCommand : lpc +deactivate ListProcCommand +'ListProcCommand column' + +LogicManager <-- AddressBookParser : lpc +deactivate AddressBookParser + +LogicManager -> ListProcCommand : execute() +activate ListProcCommand + +ListProcCommand -> ModelManager : getFilteredClientList() +activate ModelManager +ListProcCommand <-- ModelManager : lastShownList +deactivate ModelManager + +ListProcCommand -> ModelManager : setProcedures(targetClient) +activate ModelManager +deactivate ModelManager + +ListProcCommand -> ModelManager : updateFilteredProcedureList\n(targetClient, predicate) +activate ModelManager +deactivate ModelManager + +LogicManager <-- ListProcCommand +deactivate ListProcCommand +MainWindow <-- LogicManager +deactivate LogicManager + +destroy ListProcCommand +'updateUi self invocation from MainWindow' +MainWindow -> MainWindow : updateUi() +activate MainWindow +MainWindow --> MainWindow + +'End of MainWindow' +deactivate MainWindow + +@enduml diff --git a/docs/diagrams/ListProcOnCommandClassDiagram.puml b/docs/diagrams/ListProcOnCommandClassDiagram.puml new file mode 100644 index 00000000000..a4b5332b417 --- /dev/null +++ b/docs/diagrams/ListProcOnCommandClassDiagram.puml @@ -0,0 +1,17 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor LOGIC_COLOR_T4 +skinparam classBackgroundColor LOGIC_COLOR + +class AddressBookParser +class ListProcOnCommandParser +class ListProcOnCommand { + - targetDate:DateWithoutTime +} +show ListProcOnCommand attribute + +AddressBookParser ..> ListProcOnCommandParser +ListProcOnCommandParser ..> ListProcOnCommand + +@enduml diff --git a/docs/diagrams/ListProcOnCommandSequenceDiagram.puml b/docs/diagrams/ListProcOnCommandSequenceDiagram.puml new file mode 100644 index 00000000000..4385d26b3fa --- /dev/null +++ b/docs/diagrams/ListProcOnCommandSequenceDiagram.puml @@ -0,0 +1,43 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":ListProcOnCommand" as ListProcOnCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "model:Model" as Model MODEL_COLOR +participant ":Client" as Client MODEL_COLOR +end box + +-> ListProcOnCommand : execute(model) +activate ListProcOnCommand +ListProcOnCommand -> Model : getFilteredClientList() +activate Model +ListProcOnCommand <-- Model : lastShownList +deactivate Model + loop for every Client in lastShownList + ListProcOnCommand -> Client : getProcsOnDate(targetDate) + activate Client + ListProcOnCommand <-- Client : clientProceduresOnDate + deactivate Client + 'Hidden arrow to position the destroy marker below the end of the activation bar. + Client -[hidden]-> Model + destroy Client + ListProcOnCommand -> ListProcOnCommand : add(clientProceduresOnDate) + note right + The lower-level details of adding up + clientProceduresOnDate for each Client + has been abstracted to make the diagram clearer + end note + end +<-- ListProcOnCommand : CommandResult +deactivate ListProcOnCommand +note right + The interaction with CommandResult class + has been omitted to make the diagram clearer +end note +'Hidden arrow to position the destroy marker below the end of the activation bar. +ListProcOnCommand -[hidden]-> Model +destroy ListProcOnCommand +@enduml diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 4439108973a..f530e7177ef 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -12,14 +12,19 @@ Class AddressBook Class ModelManager Class UserPrefs -Class UniquePersonList -Class Person +Class UniqueClientList +Class Client Class Address +Class Plan Class Email Class Name Class Phone Class Tag - +Class Procedure +class Information +class Cost +class Completion +class Date } Class HiddenOutside #FFFFFF @@ -34,17 +39,24 @@ ModelManager -left-> "1" AddressBook ModelManager -right-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs -AddressBook *--> "1" UniquePersonList -UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag +AddressBook *--> "1" UniqueClientList +UniqueClientList --> "~* all" Client +Client *--> Name +Client *--> Phone +Client *--> Email +Client *--> Plan +Client *--> Address +Client *--> "*" Tag +Client *--> "*" Procedure + +Procedure *--> Information +Procedure *--> Cost +Procedure *--> Date +Procedure *--> Completion Name -[hidden]right-> Phone Phone -[hidden]right-> Address Address -[hidden]right-> Email -ModelManager -->"~* filtered" Person +ModelManager -->"~* filtered" Client @enduml diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 760305e0e58..59b841c1b18 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -18,8 +18,9 @@ package "AddressBook Storage" #F4F6F6{ Class "<>\nAddressBookStorage" as AddressBookStorage Class JsonAddressBookStorage Class JsonSerializableAddressBook -Class JsonAdaptedPerson +Class JsonAdaptedClient Class JsonAdaptedTag +Class JsonAdaptedProcedure } } @@ -37,7 +38,8 @@ Storage -right-|> AddressBookStorage JsonUserPrefsStorage .up.|> UserPrefsStorage JsonAddressBookStorage .up.|> AddressBookStorage JsonAddressBookStorage ..> JsonSerializableAddressBook -JsonSerializableAddressBook --> "*" JsonAdaptedPerson -JsonAdaptedPerson --> "*" JsonAdaptedTag +JsonSerializableAddressBook --> "*" JsonAdaptedClient +JsonAdaptedClient --> "*" JsonAdaptedTag +JsonAdaptedClient --> "*" JsonAdaptedProcedure @enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 95473d5aa19..130cb7c4daf 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -11,8 +11,10 @@ Class UiManager Class MainWindow Class HelpWindow Class ResultDisplay -Class PersonListPanel -Class PersonCard +Class ClientListPanel +Class ClientCard +Class ProcedureListPanel +Class ProcedureCard Class StatusBarFooter Class CommandBox } @@ -32,26 +34,31 @@ UiManager .left.|> Ui UiManager -down-> "1" MainWindow MainWindow *-down-> "1" CommandBox MainWindow *-down-> "1" ResultDisplay -MainWindow *-down-> "1" PersonListPanel +MainWindow *-down-> "1" ClientListPanel +MainWindow *-down-> "1" ProcedureListPanel MainWindow *-down-> "1" StatusBarFooter MainWindow --> "0..1" HelpWindow -PersonListPanel -down-> "*" PersonCard +ClientListPanel -down-> "*" ClientCard +ProcedureListPanel -down-> "*" ProcedureCard MainWindow -left-|> UiPart ResultDisplay --|> UiPart CommandBox --|> UiPart -PersonListPanel --|> UiPart -PersonCard --|> UiPart +ClientListPanel --|> UiPart +ClientCard --|> UiPart +ProcedureListPanel --|> UiPart +ProcedureCard --|> UiPart StatusBarFooter --|> UiPart HelpWindow --|> UiPart -PersonCard ..> Model +ClientCard ..> Model +ProcedureCard ..> Model UiManager -right-> Logic MainWindow -left-> Logic -PersonListPanel -[hidden]left- HelpWindow +ClientListPanel -[hidden]left- HelpWindow HelpWindow -[hidden]left- CommandBox CommandBox -[hidden]left- ResultDisplay ResultDisplay -[hidden]left- StatusBarFooter diff --git a/docs/diagrams/calculatepart1.puml b/docs/diagrams/calculatepart1.puml new file mode 100644 index 00000000000..e675d8793df --- /dev/null +++ b/docs/diagrams/calculatepart1.puml @@ -0,0 +1,44 @@ +@startuml + +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":CalculateCommandParser" as CalculateCommandParser LOGIC_COLOR +participant "cc:CalculateCommand" as CalculateCommand LOGIC_COLOR +end box + +[-> LogicManager : execute("calculate 22/03/2022") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("calculate 22/03/2022") +activate AddressBookParser + +create CalculateCommandParser +AddressBookParser -> CalculateCommandParser +activate CalculateCommandParser + +CalculateCommandParser --> AddressBookParser +deactivate CalculateCommandParser + +AddressBookParser -> CalculateCommandParser : parse("22/03/2022") +activate CalculateCommandParser + +create CalculateCommand +CalculateCommandParser -> CalculateCommand +activate CalculateCommand + +CalculateCommand --> CalculateCommandParser : cc +deactivate CalculateCommand + +CalculateCommandParser --> AddressBookParser : cc +deactivate CalculateCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +CalculateCommandParser -[hidden]-> AddressBookParser +destroy CalculateCommandParser + +AddressBookParser --> LogicManager : cc +deactivate AddressBookParser + +@enduml diff --git a/docs/diagrams/calculatepart2.puml b/docs/diagrams/calculatepart2.puml new file mode 100644 index 00000000000..28c1cfb81b9 --- /dev/null +++ b/docs/diagrams/calculatepart2.puml @@ -0,0 +1,58 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR + +participant "totalCost:BigDecimal" as totalCost LOGIC_COLOR +participant "cc:CalculateCommand" as CalculateCommand LOGIC_COLOR +participant "cr:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +LogicManager -> CalculateCommand : execute() +activate CalculateCommand + +CalculateCommand -> Model : getFiltereredClientList() +activate Model + + +Model --> CalculateCommand : lastshownlist +deactivate Model + +Create totalCost +CalculateCommand -> totalCost : BigDecimal() +activate totalCost + +loop number of clients +CalculateCommand -> totalCost : add cost of procedures on \nspecified date for each client + +end +totalCost --> CalculateCommand : totalCost +deactivate totalCost + +'Hidden arrow to position the destroy marker below the end of the activation bar. +totalCost -[hidden]-> CalculateCommand +destroy totalCost + + + +create CommandResult +CalculateCommand -> CommandResult +activate CommandResult + +CommandResult --> CalculateCommand :cr +deactivate CommandResult + +CalculateCommand --> LogicManager : cr +deactivate CalculateCommand +'Hidden arrow to position the destroy marker below the end of the activation bar. +CalculateCommand -[hidden]-> LogicManager +destroy CalculateCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/tracing/LogicSequenceDiagram.puml b/docs/diagrams/tracing/LogicSequenceDiagram.puml index fdcbe1c0ccc..718f8a47d13 100644 --- a/docs/diagrams/tracing/LogicSequenceDiagram.puml +++ b/docs/diagrams/tracing/LogicSequenceDiagram.puml @@ -13,7 +13,7 @@ create ecp abp -> ecp abp -> ecp ++: parse(arguments) create ec -ecp -> ec ++: index, editPersonDescriptor +ecp -> ec ++: index, editClientDescriptor ec --> ecp -- ecp --> abp --: command abp --> logic --: command diff --git a/docs/images/AddProcCommand.png b/docs/images/AddProcCommand.png new file mode 100644 index 00000000000..5f90709596f Binary files /dev/null and b/docs/images/AddProcCommand.png differ diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 2f1346869d0..7a962944013 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png index 1ec62caa2a5..86e1927e574 100644 Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ diff --git a/docs/images/CalculateCostState1.png b/docs/images/CalculateCostState1.png new file mode 100644 index 00000000000..4b76583f01f Binary files /dev/null and b/docs/images/CalculateCostState1.png differ diff --git a/docs/images/CalculateSequenceDiagram.png b/docs/images/CalculateSequenceDiagram.png new file mode 100644 index 00000000000..6c6f1d1e40c Binary files /dev/null and b/docs/images/CalculateSequenceDiagram.png differ diff --git a/docs/images/ClearCommand_After.png b/docs/images/ClearCommand_After.png new file mode 100644 index 00000000000..9af7db6ef9b Binary files /dev/null and b/docs/images/ClearCommand_After.png differ diff --git a/docs/images/ComponentManagers.png b/docs/images/ComponentManagers.png index b5764ff9273..c958e60dae1 100644 Binary files a/docs/images/ComponentManagers.png and b/docs/images/ComponentManagers.png differ diff --git a/docs/images/DeleteProcCommand_After.png b/docs/images/DeleteProcCommand_After.png new file mode 100644 index 00000000000..3f1332198af Binary files /dev/null and b/docs/images/DeleteProcCommand_After.png differ diff --git a/docs/images/DeleteProcCommand_Before.png b/docs/images/DeleteProcCommand_Before.png new file mode 100644 index 00000000000..0fc61f8ed4c Binary files /dev/null and b/docs/images/DeleteProcCommand_Before.png differ diff --git a/docs/images/DeleteProcSequenceDiagram.png b/docs/images/DeleteProcSequenceDiagram.png new file mode 100644 index 00000000000..1daea3415dd Binary files /dev/null and b/docs/images/DeleteProcSequenceDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index fa327b39618..8a8349ec2d0 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/EditClient_After.png b/docs/images/EditClient_After.png new file mode 100644 index 00000000000..aab18936d9e Binary files /dev/null and b/docs/images/EditClient_After.png differ diff --git a/docs/images/EditProcCommand_After.png b/docs/images/EditProcCommand_After.png new file mode 100644 index 00000000000..5a794304f23 Binary files /dev/null and b/docs/images/EditProcCommand_After.png differ diff --git a/docs/images/EditProcCommand_Before.png b/docs/images/EditProcCommand_Before.png new file mode 100644 index 00000000000..0e0091eb69b Binary files /dev/null and b/docs/images/EditProcCommand_Before.png differ diff --git a/docs/images/EditProcSequenceDiagram.png b/docs/images/EditProcSequenceDiagram.png new file mode 100644 index 00000000000..0e22369b294 Binary files /dev/null and b/docs/images/EditProcSequenceDiagram.png differ diff --git a/docs/images/ListProcCommandExample1.png b/docs/images/ListProcCommandExample1.png new file mode 100644 index 00000000000..77e6e458c41 Binary files /dev/null and b/docs/images/ListProcCommandExample1.png differ diff --git a/docs/images/ListProcCommandSequenceDiagram.png b/docs/images/ListProcCommandSequenceDiagram.png new file mode 100644 index 00000000000..a4344f99125 Binary files /dev/null and b/docs/images/ListProcCommandSequenceDiagram.png differ diff --git a/docs/images/ListProcOnCommandClassDiagram.png b/docs/images/ListProcOnCommandClassDiagram.png new file mode 100644 index 00000000000..6a8504777e9 Binary files /dev/null and b/docs/images/ListProcOnCommandClassDiagram.png differ diff --git a/docs/images/ListProcOnCommandSequenceDiagram.png b/docs/images/ListProcOnCommandSequenceDiagram.png new file mode 100644 index 00000000000..06e25f21b5d Binary files /dev/null and b/docs/images/ListProcOnCommandSequenceDiagram.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 04070af60d8..fc1186ded2d 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 2533a5c1af0..30596d8479d 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/UIGuide.png b/docs/images/UIGuide.png new file mode 100644 index 00000000000..73de9fa5091 Binary files /dev/null and b/docs/images/UIGuide.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5bd77847aa2..4b03685cacc 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png index 785e04dbab4..997457fd4f8 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/aceszhenwei.png b/docs/images/aceszhenwei.png new file mode 100644 index 00000000000..608a525abbb Binary files /dev/null and b/docs/images/aceszhenwei.png differ diff --git a/docs/images/addClient.png b/docs/images/addClient.png new file mode 100644 index 00000000000..d945b2fc7f7 Binary files /dev/null and b/docs/images/addClient.png differ diff --git a/docs/images/addClientGUI.png b/docs/images/addClientGUI.png new file mode 100644 index 00000000000..e676a9570e2 Binary files /dev/null and b/docs/images/addClientGUI.png differ diff --git a/docs/images/addProc.png b/docs/images/addProc.png new file mode 100644 index 00000000000..f537244cc3e Binary files /dev/null and b/docs/images/addProc.png differ diff --git a/docs/images/addProcGUI.png b/docs/images/addProcGUI.png new file mode 100644 index 00000000000..71992f611c5 Binary files /dev/null and b/docs/images/addProcGUI.png differ diff --git a/docs/images/calculate.png b/docs/images/calculate.png new file mode 100644 index 00000000000..6a61bb43fa7 Binary files /dev/null and b/docs/images/calculate.png differ diff --git a/docs/images/calculatepart1.png b/docs/images/calculatepart1.png new file mode 100644 index 00000000000..6de09777507 Binary files /dev/null and b/docs/images/calculatepart1.png differ diff --git a/docs/images/calculatepart2.png b/docs/images/calculatepart2.png new file mode 100644 index 00000000000..e3cb62c3f0d Binary files /dev/null and b/docs/images/calculatepart2.png differ diff --git a/docs/images/deleteClient.png b/docs/images/deleteClient.png new file mode 100644 index 00000000000..2841134cdf2 Binary files /dev/null and b/docs/images/deleteClient.png differ diff --git a/docs/images/deleteClientGUI.png b/docs/images/deleteClientGUI.png new file mode 100644 index 00000000000..3350cd89ff3 Binary files /dev/null and b/docs/images/deleteClientGUI.png differ diff --git a/docs/images/deleteProc.png b/docs/images/deleteProc.png new file mode 100644 index 00000000000..9460a199f56 Binary files /dev/null and b/docs/images/deleteProc.png differ diff --git a/docs/images/findCommandExample.png b/docs/images/findCommandExample.png new file mode 100644 index 00000000000..24c7da4d171 Binary files /dev/null and b/docs/images/findCommandExample.png differ diff --git a/docs/images/iztanpy.png b/docs/images/iztanpy.png new file mode 100644 index 00000000000..2ebcd9f5141 Binary files /dev/null and b/docs/images/iztanpy.png differ diff --git a/docs/images/jbkim1999.png b/docs/images/jbkim1999.png new file mode 100644 index 00000000000..a565efcdede Binary files /dev/null and b/docs/images/jbkim1999.png differ diff --git a/docs/images/list.png b/docs/images/list.png new file mode 100644 index 00000000000..b770d4da018 Binary files /dev/null and b/docs/images/list.png differ diff --git a/docs/images/listGUI.png b/docs/images/listGUI.png new file mode 100644 index 00000000000..3310eabc5db Binary files /dev/null and b/docs/images/listGUI.png differ diff --git a/docs/images/listProcOn.png b/docs/images/listProcOn.png new file mode 100644 index 00000000000..472e6f0bf0c Binary files /dev/null and b/docs/images/listProcOn.png differ diff --git a/docs/images/listProcOnNew.png b/docs/images/listProcOnNew.png new file mode 100644 index 00000000000..7f1ee59e896 Binary files /dev/null and b/docs/images/listProcOnNew.png differ diff --git a/docs/images/mark.png b/docs/images/mark.png new file mode 100644 index 00000000000..60ca3a54633 Binary files /dev/null and b/docs/images/mark.png differ diff --git a/docs/images/robinrojh.png b/docs/images/robinrojh.png new file mode 100644 index 00000000000..9d94ea02e00 Binary files /dev/null and b/docs/images/robinrojh.png differ diff --git a/docs/images/teominghuang.png b/docs/images/teominghuang.png new file mode 100644 index 00000000000..283e1025579 Binary files /dev/null and b/docs/images/teominghuang.png differ diff --git a/docs/images/unmark.png b/docs/images/unmark.png new file mode 100644 index 00000000000..a28e6078fb3 Binary files /dev/null and b/docs/images/unmark.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..a2c776ee4ad 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,16 @@ --- layout: page -title: AddressBook Level-3 +title: Networkers --- -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) -[![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3) +[![CI Status](https://github.com/AY2122S2-CS2103T-W13-1/tp/actions/workflows/gradle.yml/badge.svg?branch=master)](https://github.com/AY2122S2-CS2103T-W13-1/tp/actions/workflows/gradle.yml)[![codecov](https://codecov.io/gh/AY2122S2-CS2103T-W13-1/tp/branch/master/graph/badge.svg?token=OEVG5AG88Y)](https://codecov.io/gh/AY2122S2-CS2103T-W13-1/tp) ![Ui](images/Ui.png) -**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). +**Networkers is a desktop application to assist network technicians in managing their client details and relevant preparation for future servicings.** In addition to having the benefits of a Graphical User Interface (GUI), it is also optimised for use with a Command Line Interface (CLI). -* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). -* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. +* If you are interested in using Networkers, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). +* If you are interested in developing Networkers, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** diff --git a/docs/team/aceszhenwei.md b/docs/team/aceszhenwei.md new file mode 100644 index 00000000000..d03fb0bcf56 --- /dev/null +++ b/docs/team/aceszhenwei.md @@ -0,0 +1,43 @@ +--- +layout: page +title: Alexander's Project Portfolio Page +--- + +### Project: Networkers + +Networkers is a desktop app for managing contacts for networks, optimised for use via a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, Networkers can get your contact management tasks done faster than traditional GUI apps. + +Given below are my contributions to the project. + +### Summary of my contributions + +#### Code contributed: +[RepoSense Link](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=aceszhenwei&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2022-02-18&tabOpen=true&tabType=authorship&tabAuthor=aceszhenwei&tabRepo=AY2122S2-CS2103T-W13-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=&authorshipIsBinaryFileTypeChecked=false) + +#### Enhancements implemented: + * Refactored the project to take in Clients instead of Persons, as well as the Clients' specific attributes. + * Implemented an autosort feature when Procedures are added. + * When new procedures are added, they will now be auto-sorted according to their dates. + * Created relevant tests that are randomly generated for the auto-sort function. + +#### Documentation: + * User Guide: + * Added documentation for the following features in v1.3: + * `find` + * `clear` + * `exit` + * Updated documentation for other features as well. + * Added new UI images for each command to better represent Networkers. + * Cleaned up most of the user guide bugs reported after Practical Exam Dry Run. + + * Developer Guide: + * Added Non-Functional Requirements (NFRs). + * Wrote AddProc Command and its respective sequence diagram. + * Wrote EditProc Command's Use Case + +#### Review/mentoring contributions +[Pull Requests Reviewed by me](https://github.com/AY2122S2-CS2103T-W13-1/tp/pulls?q=is%3Apr+reviewed-by%3Aaceszhenwei) + +#### Contributions to team-based tasks: + * Sorted and tagged all issues that were raised after Practical Exam Dry Run. + * Hosted the weekly team meetings on Zoom diff --git a/docs/team/iztanpy.md b/docs/team/iztanpy.md new file mode 100644 index 00000000000..eea907d4302 --- /dev/null +++ b/docs/team/iztanpy.md @@ -0,0 +1,54 @@ +--- +layout: page +title: Isaac Tan's Project Portfolio Page +--- +## Project: Networkers + +### Overview + +Networkers is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. + +Given below are my contributions to the project. + +### Summary of my contributions + + +- **Code contributed:** [RepoSense link](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=iztanpy&breakdown=true&sort=groupTitle&sortWithin=title&since=2022-02-18&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + + +- **Enhancements implemented:** + * Created `Procedure` Class as well as related attribute classes. This included + * `Date` Class + * `Cost` Class + * `Information` Class + * Created the `Calculate` command and parser, which calculates the cost of all procedures + on a specified date. + * Created the relevant tests for `Procedure` along with its accompanying classes + * Created tests for `Calculate` as well as its parser. + * Generally cleaned up the code within the application to better fit our theme + + +- **Contributions to the UG:** + * Introduction + * Quick Start + * `AddClient` Command + * `Calculate` Command + * Use cases for `AddClient` and `Calculate` Commands +
+ +- **Contributions to the DG:** + * General standardisation of format and language + * Heavily involved in the editing of the User Guide before Feature Freeze. + * Wrote the introduction and quick start for the DG + * Maintained and created `Calculate` Command + * Worked with Ben to edit the Model architecture in the User Guide (Procedure-specific). + + +- **Contributions to team-based tasks** + * Created jar file for first 2 iterations + + +- **Review/mentoring contributions:** + * Well involved in the reviewing of team members code, which can be accesed [here](https://github.com/AY2122S2-CS2103T-W13-1/tp/pulls?q=is%3Apr+reviewed-by%3A%40me+is%3Aclosed) + + diff --git a/docs/team/jbkim1999.md b/docs/team/jbkim1999.md new file mode 100644 index 00000000000..3f0f185e7a1 --- /dev/null +++ b/docs/team/jbkim1999.md @@ -0,0 +1,50 @@ +--- +layout: page +title: JB Kim's Project Portfolio Page +--- + +## Project: Networkers + +Networkers is a desktop app for managing contacts for network technicians, optimised for use via a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, Networkers can get your contact management tasks done faster than traditional GUI apps. + +Given below are my contributions to the project. + +### Summary of my contributions + +- **New Feature**: Added a feature in which the users can add a Procedure to an existing Client. + - Justification: Users may wish to record what Procedures (such as installing modem, connecting devices) they have done, or will be done to the Client. + - Highlights: Making use of `Model` component to delegate the update, thereby adhering to the OOP principle. + +- **New Feature**: Added a feature in which the users can list down all the Procedures scheduled on a specified date. + - Justification: Users may wish to know what Procedures they have performed on a particular date, or also generate a to-do list for future Procedures. + - Highlights: Making use of `Pair` container to connect each Procedure to an associated Client, and `Stream` feature for iteration. + +- **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=jbkim1999&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&tabOpen=true&tabType=zoom&zA=jbkim1999&zR=AY2122S2-CS2103T-W13-1%2Ftp%5Bmaster%5D&zACS=237.07692307692307&zS=2022-02-18&zFS=jbkim1999&zU=2022-04-07&zMG=false&zFTF=commit&zFGS=groupByRepos&zFR=false&zFT=docs~functional-code~test-code&since=2022-02-18) + +- **Enhancements implemented:** + - Added a `Plan` attribute to the Client (e.g. the name of the plan and bandwidth that a Client has subscribed for). + - Modifications were made, especially on the test cases, to handle the regressions caused. + +- **Contributions to the UG:** + - Added documentations for `AddProc`, `ListProcOn` commands + - Revised general formats of the UG + - Divided the feature section into Format, Note, and Example subsections for enhanced readability. + +- **Contributions to the DG:** + - Reflected the architectural design (such as diagrams) changes + - Renamed some classes involving `Person` to `Client`. + - Added in classes as a result of having additional `Procedure` class. + - Described the implementation of `ListProcOnCommand` + - Made use of class and sequence diagram to illustrate the enhancement. + +- **Contributions to team-based tasks** + - Conducted product demo for iteration v1.2 and v1.3 + +- **Review/mentoring contributions:** + - PRs reviewed (with non-trivial review comments): + - [#73](https://github.com/AY2122S2-CS2103T-W13-1/tp/pull/73), [#88](https://github.com/AY2122S2-CS2103T-W13-1/tp/pull/88), [#99](https://github.com/AY2122S2-CS2103T-W13-1/tp/pull/99) + - ... and more (can be found under [tP comments dashboard](https://nus-cs2103-ay2122s2.github.io/dashboards/contents/tp-comments.html)) + +- **Contributions beyond the project team:** + - Forum participation count as commenter: [9](https://github.com/nus-cs2103-AY2122S2/forum/issues?q=commenter%3Ajbkim1999) + - Forum participation count as author: [4](https://github.com/nus-cs2103-AY2122S2/forum/issues?q=author%3Ajbkim1999) diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md deleted file mode 100644 index 773a07794e2..00000000000 --- a/docs/team/johndoe.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -layout: page -title: John Doe's Project Portfolio Page ---- - -### Project: AddressBook Level 3 - -AddressBook - Level 3 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. - -Given below are my contributions to the project. - -* **New Feature**: Added the ability to undo/redo previous commands. - * What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command. - * Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them. - * Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands. - * Credits: *{mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}* - -* **New Feature**: Added a history command that allows the user to navigate to previous commands using up/down keys. - -* **Code contributed**: [RepoSense link]() - -* **Project management**: - * Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub - -* **Enhancements to existing features**: - * Updated the GUI color scheme (Pull requests [\#33](), [\#34]()) - * Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests [\#36](), [\#38]()) - -* **Documentation**: - * User Guide: - * Added documentation for the features `delete` and `find` [\#72]() - * Did cosmetic tweaks to existing documentation of features `clear`, `exit`: [\#74]() - * Developer Guide: - * Added implementation details of the `delete` feature. - -* **Community**: - * PRs reviewed (with non-trivial review comments): [\#12](), [\#32](), [\#19](), [\#42]() - * Contributed to forum discussions (examples: [1](), [2](), [3](), [4]()) - * Reported bugs and suggestions for other teams in the class (examples: [1](), [2](), [3]()) - * Some parts of the history feature I added was adopted by several other class mates ([1](), [2]()) - -* **Tools**: - * Integrated a third party library (Natty) to the project ([\#42]()) - * Integrated a new Github plugin (CircleCI) to the team repo - -* _{you can add/remove categories in the list above}_ diff --git a/docs/team/robinrojh.md b/docs/team/robinrojh.md new file mode 100644 index 00000000000..8478a3db312 --- /dev/null +++ b/docs/team/robinrojh.md @@ -0,0 +1,69 @@ +--- +layout: page +title: Yong Gi Roh's Project Portfolio Page +--- + +## Project Overview: Networkers +### Overview + +Networkers is a desktop address book application used for teaching Software Engineering principles. +The user interacts with it using a CLI, and it has a GUI created with JavaFX. +It is written in Java, and has about 10 kLoC. + +Below are my contributions to the project. + +### Summary of my contributions + +- **Code contributed:** [My Contribution](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2022-02-18&tabOpen=true&tabType=authorship&tabAuthor=robinrojh&tabRepo=AY2122S2-CS2103T-W13-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false) +- **Pull Requests created by me:** [PR](https://github.com/AY2122S2-CS2103T-W13-1/tp/pulls?q=is%3Apr+assignee%3Arobinrojh+) + +#### Enhancements implemented: + +- `listProc` Command + - Added test cases +- Handle UI-related problems and enhancements + - Divided the UI into two columns, one for Clients and another for Procedures + - Display the whether a Procedure is completed +- Mark and Unmark Command + - Allowed users to mark and unmark a procedure as complete + - Reflect changes in `completed` field of the Procedure +- JsonAdaptedProcedure + - Created the JsonAdaptedProcedure to store Procedures as a json +- Single-worded Command Parsing and Validation + - Prevented single-worded commands like `help` and `exit` to throw error messages + +#### Contributions to the UG + +- Section: **View All Clients: `list`** +- Section: **View All Procedures of a Client: `listProc`** + - Added an overview of the method + - Added sequence diagram and design consideration +- Small Sections: **Clear, Exit** +- Command Summary Table Maintenance + +#### Contributions to the DG + +- Section: **List Procedures By Client (ListProc)** +- Architecture sequence diagram maintenance +- User stories for `list` and `listProc` Command +- Some Non-Functional Requirements (NFR) +- Section: Appendix updated to match current version + +#### Contributions to team-based tasks + +- Created team repository and setup GitHub pages with Jekyll +- Participated in weekly meetings to receive & provide feedback and decide next steps + +#### Review/mentoring contributions + +- [Pull Requests Reviewed by me](https://github.com/AY2122S2-CS2103T-W13-1/tp/pulls?q=is%3Apr+reviewed-by%3Arobinrojh+) + +#### Excerpts from DG + +- ListProc Sequence Diagram: + +![listProc Sequence Diagram](../images/ListProcCommandSequenceDiagram.png) + +- Colors for Component Managers: + +![Components Diagram](../images/ComponentManagers.png) diff --git a/docs/team/teominghuang.md b/docs/team/teominghuang.md new file mode 100644 index 00000000000..8b99f1c359b --- /dev/null +++ b/docs/team/teominghuang.md @@ -0,0 +1,63 @@ +--- +layout: page +title: Teo Ming Huang's Project Portfolio Page +--- + +## Project: Networkers + +### Overview + +Networkers is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. + +Given below are my contributions to the project. + +### Summary of my contributions + +- **Code contributed:** + - **Code contributed:** [My Contribution](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=teominghuang&breakdown=true&sort=groupTitle&sortWithin=title&since=2022-02-18&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + - **Pull Requests created by me:** [PR](https://github.com/AY2122S2-CS2103T-W13-1/tp/pulls?q=is%3Apr+assignee%3Ateominghuang) +- **Enhancements implemented:** + + * Created the following attribute class that belongs to `Procedure`, and its following testcases: + * `Completion` Class + * Connected `Procedure` to the different dependencies. This includes: + * Change testcases and toModelType methods to include Procedure + * Edit `JsonAdaptedClient` to ensure that it includes `JsonAdaptedProcedure` + * Created `DeleteProcCommand` and the following accompanying features: + * Create `DeleteProcCommandParser` + * Create Sequence diagram for `EditProcCommand`. + * Create test cases for both `DeleteProcCommand` and `DeleteProcCommandParser` + * Maintain coherence of feature in the User Guide and the Developer Guide. + * Created `EditProcCommand` and the following accompanying features: + * Create `EditProcCommandParser` + * Create Sequence diagram for `EditProcCommand`. + * Create test cases for both `EditProcCommand` and `EditProcCommandParser` + * Maintain coherence of feature in the User Guide and the Developer Guide. + * Create Default values for `Procedure`. + * Maintenance of User Guide and Developer Guide. + +- **Contributions to the UG:** + * General standardisation of format and language + * Heavily involved in the editing of the User Guide before Final Submission. + * Heavily involved in the editing of the User Guide before Feature Freeze. + * Worked with Isaac to edit the Model architecture in the User Guide (Procedure-specific). + * Maintained `EditClient` Command + * Maintained `DeleteProcCommand` Command + * Maintained `EditProcCommand` Command + +- **Contributions to the DG:** + * General standardisation of format and language + * Heavily involved in the editing of the User Guide before Final Submission. + * Maintained `DeleteProcCommand` Command + * Maintained `EditProcCommand` Command + +- **Contributions to team-based tasks** + * Created jar file for end of v1.3. + * Helped to tag final commit for v1.4. + * Helped to wrap up and closed two milestones. + * Consistently drafted agendas for meetings. + * Collated, pinned, and maintained important links in the telegram group check. + +- **Review/mentoring contributions:** + - [Pull Requests Reviewed by me](https://github.com/AY2122S2-CS2103T-W13-1/tp/pulls?q=is%3Apr+reviewed-by%3Ateominghuang) + - More tP comments by me can be found in the [dashboard](https://nus-cs2103-ay2122s2.github.io/dashboards/contents/tp-comments.html) diff --git a/preferences.json b/preferences.json new file mode 100644 index 00000000000..dc010b00824 --- /dev/null +++ b/preferences.json @@ -0,0 +1,11 @@ +{ + "guiSettings" : { + "windowWidth" : 960.0, + "windowHeight" : 720.0, + "windowCoordinates" : { + "x" : 288, + "y" : 64 + } + }, + "addressBookFilePath" : "data\\networkers.json" +} diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/address/commons/core/GuiSettings.java index ba33653be67..56c008ba585 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/address/commons/core/GuiSettings.java @@ -10,8 +10,8 @@ */ public class GuiSettings implements Serializable { - private static final double DEFAULT_HEIGHT = 600; - private static final double DEFAULT_WIDTH = 740; + private static final double DEFAULT_HEIGHT = 720; + private static final double DEFAULT_WIDTH = 960; private final double windowWidth; private final double windowHeight; diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java index 1deb3a1e469..85218b7bde4 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/seedu/address/commons/core/Messages.java @@ -4,10 +4,19 @@ * Container for user visible messages. */ public class Messages { - public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; + public static final String MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX = "Please provide a Client index that " + + "belongs to an existing Client."; + public static final String MESSAGE_INVALID_CLIENT_NON_POSITIVE_INDEX = "Please provide a positive client " + + "index"; + public static final String MESSAGE_INVALID_PROCEDURE_DISPLAYED_INDEX = + "Please provide a Procedure index that belongs to an existing Procedure."; + public static final String MESSAGE_INVALID_PROCEDURE_NON_POSITIVE_INDEX = "Please provide a positive " + + "procedure index"; + public static final String MESSAGE_INVALID_PROCEDURE_DUPLICATED = "The Client already has this Procedure." + + "\nTry creating a Procedure of different information, date, time, cost, or client."; + public static final String MESSAGE_CALCULATE_COST_SUCCESS = "Total Cost: %1$s"; + public static final String MESSAGE_CLIENTS_LISTED_OVERVIEW = "%1$d client(s) listed!"; } diff --git a/src/main/java/seedu/address/commons/core/datewithouttime/DateWithoutTime.java b/src/main/java/seedu/address/commons/core/datewithouttime/DateWithoutTime.java new file mode 100644 index 00000000000..61e25d75d34 --- /dev/null +++ b/src/main/java/seedu/address/commons/core/datewithouttime/DateWithoutTime.java @@ -0,0 +1,70 @@ +package seedu.address.commons.core.datewithouttime; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.format.ResolverStyle; + +/** + * Represents a Procedure's date in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidDate(String)} + */ +public class DateWithoutTime { + + public static final String MESSAGE_CONSTRAINTS = + "Please ensure that you have typed an existing date in the correct format. \n" + + "Dates should be in the format DD/MM/YYYY, and it should not be blank \n" + + "Year entered must be less than 10000"; + /* + * The first character of the address must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "^(0?[1-9]|[12][0-9]|3[01])[\\/\\-](0?[1-9]|1[012])[\\/\\-]\\d{4}$"; + + public static final DateTimeFormatter FORMAT_WITH_DATE = DateTimeFormatter.ofPattern("dd/MM/uuuu"); + + public final LocalDate validDate; + + /** + * Constructs a {@code Date}. + * + * @param date A valid date. + */ + public DateWithoutTime(String date) { + requireNonNull(date); + checkArgument(isValidDate(date), MESSAGE_CONSTRAINTS); + validDate = LocalDate.parse(date, FORMAT_WITH_DATE); // need to check + } + + /** + * Returns true if a given string is a valid date. + */ + public static boolean isValidDate(String test) { + if (test.matches(VALIDATION_REGEX)) { + try { + LocalDate.parse(test, FORMAT_WITH_DATE + .withResolverStyle(ResolverStyle.STRICT)); + return true; + } catch (DateTimeParseException err) { + return false; + } + } + return false; + } + + @Override + public String toString() { + return validDate.format(FORMAT_WITH_DATE); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DateWithoutTime // instanceof handles nulls + && validDate.equals(((DateWithoutTime) other).validDate)); // state check + } +} + diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java index 92cd8fa605a..6b3757a33bd 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/address/logic/Logic.java @@ -8,7 +8,8 @@ import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.client.Client; +import seedu.address.model.procedure.Procedure; /** * API of the Logic component @@ -30,8 +31,12 @@ public interface Logic { */ ReadOnlyAddressBook getAddressBook(); - /** Returns an unmodifiable view of the filtered list of persons */ - ObservableList getFilteredPersonList(); + + /** Returns an unmodifiable view of the filtered list of clients */ + ObservableList getFilteredClientList(); + + /** Returns an unmodifiable view of the filtered list of procedures */ + ObservableList getFilteredProcedureList(); /** * Returns the user prefs' address book file path. diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 9d9c6d15bdc..87b3a11a7dd 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -14,7 +14,8 @@ import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.Model; import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.client.Client; +import seedu.address.model.procedure.Procedure; import seedu.address.storage.Storage; /** @@ -40,7 +41,6 @@ public LogicManager(Model model, Storage storage) { @Override public CommandResult execute(String commandText) throws CommandException, ParseException { logger.info("----------------[USER COMMAND][" + commandText + "]"); - CommandResult commandResult; Command command = addressBookParser.parseCommand(commandText); commandResult = command.execute(model); @@ -60,8 +60,13 @@ public ReadOnlyAddressBook getAddressBook() { } @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); + public ObservableList getFilteredClientList() { + return model.getFilteredClientList(); + } + + @Override + public ObservableList getFilteredProcedureList() { + return model.getFilteredProcedureList(); } @Override diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddClientCommand.java similarity index 52% rename from src/main/java/seedu/address/logic/commands/AddCommand.java rename to src/main/java/seedu/address/logic/commands/AddClientCommand.java index 71656d7c5c8..3ea3f587f2b 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddClientCommand.java @@ -5,63 +5,66 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PLAN; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.address.model.client.Client; /** - * Adds a person to the address book. + * Adds a client to the address book. */ -public class AddCommand extends Command { +public class AddClientCommand extends Command { - public static final String COMMAND_WORD = "add"; + public static final String COMMAND_WORD = "addClient"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a client to the address book. " + "Parameters: " + PREFIX_NAME + "NAME " + PREFIX_PHONE + "PHONE " + PREFIX_EMAIL + "EMAIL " + PREFIX_ADDRESS + "ADDRESS " + + PREFIX_PLAN + "PLAN " + "[" + PREFIX_TAG + "TAG]...\n" + "Example: " + COMMAND_WORD + " " - + PREFIX_NAME + "John Doe " + + PREFIX_NAME + "Miniso " + PREFIX_PHONE + "98765432 " - + PREFIX_EMAIL + "johnd@example.com " + + PREFIX_EMAIL + "miniso@example.com " + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; + + PREFIX_PLAN + "VALUE 50MBps " + + PREFIX_TAG + "family "; - public static final String MESSAGE_SUCCESS = "New person added: %1$s"; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; + public static final String MESSAGE_SUCCESS = "New client added: %1$s"; + public static final String MESSAGE_DUPLICATE_CLIENT = "A client with a duplicate address" + + " already exists in the address book"; - private final Person toAdd; + private final Client toAdd; /** - * Creates an AddCommand to add the specified {@code Person} + * Creates an AddClientCommand to add the specified {@code Client} */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; + public AddClientCommand(Client client) { + requireNonNull(client); + toAdd = client; } @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); + if (model.hasClient(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_CLIENT); } - model.addPerson(toAdd); + model.addClient(toAdd); return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); } @Override public boolean equals(Object other) { return other == this // short circuit if same object - || (other instanceof AddCommand // instanceof handles nulls - && toAdd.equals(((AddCommand) other).toAdd)); + || (other instanceof AddClientCommand // instanceof handles nulls + && toAdd.equals(((AddClientCommand) other).toAdd)); } } diff --git a/src/main/java/seedu/address/logic/commands/AddProcCommand.java b/src/main/java/seedu/address/logic/commands/AddProcCommand.java new file mode 100644 index 00000000000..73d0b778aa7 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddProcCommand.java @@ -0,0 +1,166 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COST; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_INFORMATION; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_CLIENTS; +import static seedu.address.model.Model.PREDICATE_SHOW_CLIENT_PROCEDURES; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.client.Address; +import seedu.address.model.client.Client; +import seedu.address.model.client.Email; +import seedu.address.model.client.Name; +import seedu.address.model.client.Phone; +import seedu.address.model.client.Plan; +import seedu.address.model.procedure.Procedure; +import seedu.address.model.tag.Tag; + + +/** + * Add a procedure of an existing client in the address book. + */ +public class AddProcCommand extends Command { + + public static final String COMMAND_WORD = "addProc"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a Procedure to the identified Client " + + "by the index number used in the displayed client list. \n" + + "Parameters: INDEX (must be a positive integer) " + + PREFIX_INFORMATION + "INFORMATION " + + PREFIX_COST + "COST " + + PREFIX_DATE + "DATE \n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_INFORMATION + "Fix Router " + + PREFIX_COST + "10.5 " + + PREFIX_DATE + "20/03/2022 11:30 "; + + public static final String MESSAGE_SUCCESS = "New Procedure added: %1$s"; // Test needed + public static final String MESSAGE_DUPLICATE_PROCEDURE = "The Client already has this Procedure." + + "\nTry adding a Procedure of different information, date, time, cost, or to a different Client."; + private final Index index; + private final Procedure procedure; + + /** + * @param index of the Client in the filtered client list to add Procedure to + * @param procedure Procedure to add to the Client + */ + public AddProcCommand(Index index, Procedure procedure) { + requireNonNull(index); + requireNonNull(procedure); + + this.index = index; + this.procedure = procedure; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredClientList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX); + } + + Client clientToAddProc = lastShownList.get(index.getZeroBased()); + Client clientWithAddedProc = null; + + try { + clientWithAddedProc = clientWithAddedProcFrom(clientToAddProc); + } catch (CommandException err) { + throw new CommandException(MESSAGE_DUPLICATE_PROCEDURE); + } + + model.setClient(clientToAddProc, clientWithAddedProc); + model.updateFilteredClientList(PREDICATE_SHOW_ALL_CLIENTS); + model.updateFilteredProcedureList(clientWithAddedProc, PREDICATE_SHOW_CLIENT_PROCEDURES); + return new CommandResult(String.format(MESSAGE_SUCCESS, procedure)); + } + + /** + * Creates and returns a {@code Client} with the details of {@code clientToAddProc} + * edited with {@code procListWithAddedProc}. + */ + private Client clientWithAddedProcFrom(Client clientToAddProc) throws CommandException { + assert clientToAddProc != null; + + Name name = clientToAddProc.getName(); + Phone phone = clientToAddProc.getPhone(); + Email email = clientToAddProc.getEmail(); + Plan plan = clientToAddProc.getPlan(); + Address address = clientToAddProc.getAddress(); + Set tags = clientToAddProc.getTags(); + List updatedProcedures = new ArrayList<>(); + + try { + updatedProcedures.addAll(procListWithAddedProc(clientToAddProc.getProcedures())); + } catch (CommandException err) { + throw new CommandException(MESSAGE_DUPLICATE_PROCEDURE); + } + + return new Client(name, phone, email, address, plan, + tags, updatedProcedures); + } + + /** + * Returns a list of Procedures that have been sorted according to their dates. + */ + public List procListWithAddedProc(List procedureList) + throws CommandException { + + List updatedProcedureList = new ArrayList<>(); + for (int i = 0; i < procedureList.size(); i++) { + if (procedureList.get(i).isProcedureDuplicate(procedure)) { + throw new CommandException(MESSAGE_DUPLICATE_PROCEDURE); + } + updatedProcedureList.add(procedureList.get(i)); + } + updatedProcedureList.add(procedure); + Comparator mapComparator = (Procedure m1, Procedure m2) -> m1.getDate() + .compareTo(m2.getDate()); + Collections.sort(updatedProcedureList, mapComparator); + return updatedProcedureList; + } + + /** + * Returns a list of Procedures, guaranteed to add a properly defined new Procedure. + * Used internally for testing. + */ + public List procListWithAddedProperProc(List procedureList) { + + List updatedProcedureList = new ArrayList<>(); + for (int i = 0; i < procedureList.size(); i++) { + updatedProcedureList.add(procedureList.get(i)); + } + updatedProcedureList.add(procedure); + return updatedProcedureList; + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AddProcCommand)) { + return false; + } + + // state check + AddProcCommand otherCommand = (AddProcCommand) other; + return index.equals(otherCommand.index) + && procedure.equals(otherCommand.procedure); + } +} diff --git a/src/main/java/seedu/address/logic/commands/CalculateCommand.java b/src/main/java/seedu/address/logic/commands/CalculateCommand.java new file mode 100644 index 00000000000..b61059dac9c --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/CalculateCommand.java @@ -0,0 +1,69 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.math.BigDecimal; +import java.util.List; + +import seedu.address.commons.core.datewithouttime.DateWithoutTime; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.client.Client; + + +/** + * Get cost from a givenDate + */ +public class CalculateCommand extends Command { + + public static final String COMMAND_WORD = "calculate"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Calculates the total cost of all procedures on a given date\n" + + "Parameters: date (must be a valid date)\n" + + "Example: " + COMMAND_WORD + " 02/02/2022"; + + public static final String MESSAGE_CALCULATE_COST_SUCCESS = "Total Cost: %1$s"; + + public static final String MESSAGE_INVALID_DATE_FAILURE = "Invalid date!"; + + private final DateWithoutTime targetDate; + + public CalculateCommand(DateWithoutTime date) { + this.targetDate = date; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredClientList(); + BigDecimal totalCost = new BigDecimal(0); + + for (int i = 0; i < lastShownList.size(); i++) { + Client currentClient = lastShownList.get(i); + BigDecimal currentCost = currentClient.getCostOnDate(targetDate); + totalCost = totalCost.add(currentCost); + } + totalCost.setScale(2, BigDecimal.ROUND_UP); + String totalCostString = '$' + totalCost.toPlainString(); + + return new CommandResult(String.format(MESSAGE_CALCULATE_COST_SUCCESS, totalCostString)); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof CalculateCommand)) { + return false; + } + + // state check + CalculateCommand otherCommand = (CalculateCommand) other; + return targetDate.equals(otherCommand.targetDate); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java index 9c86b1fa6e4..db58b9ee90f 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java @@ -13,6 +13,8 @@ public class ClearCommand extends Command { public static final String COMMAND_WORD = "clear"; public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Clears the address book. \n" + + "Example: clear"; @Override public CommandResult execute(Model model) { @@ -20,4 +22,9 @@ public CommandResult execute(Model model) { model.setAddressBook(new AddressBook()); return new CommandResult(MESSAGE_SUCCESS); } + + @Override + public boolean equals(Object other) { + return other instanceof ClearCommand; + } } diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteClientCommand.java similarity index 51% rename from src/main/java/seedu/address/logic/commands/DeleteCommand.java rename to src/main/java/seedu/address/logic/commands/DeleteClientCommand.java index 02fd256acba..226d7d280fc 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteClientCommand.java @@ -8,46 +8,46 @@ import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.address.model.client.Client; /** - * Deletes a person identified using it's displayed index from the address book. + * Deletes a client identified using it's displayed index from the address book. */ -public class DeleteCommand extends Command { +public class DeleteClientCommand extends Command { - public static final String COMMAND_WORD = "delete"; + public static final String COMMAND_WORD = "deleteClient"; public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Deletes the person identified by the index number used in the displayed person list.\n" + + ": Deletes the client identified by the index number used in the displayed client list.\n" + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1"; - public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; + public static final String MESSAGE_DELETE_CLIENT_SUCCESS = "Deleted Client: %1$s"; private final Index targetIndex; - public DeleteCommand(Index targetIndex) { + public DeleteClientCommand(Index targetIndex) { this.targetIndex = targetIndex; } @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); + List lastShownList = model.getFilteredClientList(); if (targetIndex.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + throw new CommandException(Messages.MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX); } - Person personToDelete = lastShownList.get(targetIndex.getZeroBased()); - model.deletePerson(personToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); + Client clientToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deleteClient(clientToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_CLIENT_SUCCESS, clientToDelete)); } @Override public boolean equals(Object other) { return other == this // short circuit if same object - || (other instanceof DeleteCommand // instanceof handles nulls - && targetIndex.equals(((DeleteCommand) other).targetIndex)); // state check + || (other instanceof DeleteClientCommand // instanceof handles nulls + && targetIndex.equals(((DeleteClientCommand) other).targetIndex)); // state check } } diff --git a/src/main/java/seedu/address/logic/commands/DeleteProcCommand.java b/src/main/java/seedu/address/logic/commands/DeleteProcCommand.java new file mode 100644 index 00000000000..f7ee70124d7 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeleteProcCommand.java @@ -0,0 +1,141 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_CLIENTS; +import static seedu.address.model.Model.PREDICATE_SHOW_CLIENT_PROCEDURES; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.client.Address; +import seedu.address.model.client.Client; +import seedu.address.model.client.Email; +import seedu.address.model.client.Name; +import seedu.address.model.client.Phone; +import seedu.address.model.client.Plan; +import seedu.address.model.procedure.Procedure; +import seedu.address.model.tag.Tag; + +/** + * Deletes an existing procedure that belongs to an existing client in the address book. + */ +public class DeleteProcCommand extends Command { + + public static final String COMMAND_WORD = "deleteProc"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Delete the Procedure under a Client " + + "by the index numbers used in the Client Panel and the Procedure Panel. " + + "Existing Procedures indicated (by the input value) will be deleted.\n" + + "Parameters: INDEX (must be a positive integer and an existing Client)\n" + + "INDEX (must be a positive integer and an existing Procedure)\n" + + "Example: " + COMMAND_WORD + " 1 2"; + + public static final String MESSAGE_DELETE_PROCEDURE_SUCCESS = "Deleted Procedure: %1$s"; + public static final String MESSAGE_DELETE_PROCEDURE_FAILURE = "Deleted Procedure unsuccessful." + + "\nTry again in a while!"; + + private final Index clientIndex; + private final Index procedureIndex; + + /** + * @param clientIndex of the client in the filtered client list, whose procedure you'd like to delete. + * @param procedureIndex of the procedure in the filtered procedure list to delete + */ + public DeleteProcCommand(Index clientIndex, Index procedureIndex) { + requireNonNull(clientIndex); + requireNonNull(procedureIndex); + + this.clientIndex = clientIndex; + this.procedureIndex = procedureIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + if (clientIndex.getZeroBased() < 0) { + throw new CommandException(Messages.MESSAGE_INVALID_CLIENT_NON_POSITIVE_INDEX); + } + List lastShownList = model.getFilteredClientList(); + + if (clientIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX); + } + + Client clientToEdit = lastShownList.get(clientIndex.getZeroBased()); + Client editedClient = null; + List clientProcedureList = clientToEdit.getProcedures(); + List procedureDeletedList = new ArrayList<>(); + + try { + procedureDeletedList = deleteProcedureFromList(clientProcedureList, procedureIndex); + } catch (UnsupportedOperationException err) { + throw new CommandException(DeleteProcCommand.MESSAGE_DELETE_PROCEDURE_FAILURE); + } + + Procedure procedureDeleted = clientProcedureList.get(procedureIndex.getZeroBased()); + editedClient = updateClientProcedures(clientToEdit, procedureDeletedList); + model.setClient(clientToEdit, editedClient); + model.updateFilteredClientList(PREDICATE_SHOW_ALL_CLIENTS); + model.updateFilteredProcedureList(editedClient, PREDICATE_SHOW_CLIENT_PROCEDURES); + return new CommandResult(String.format(MESSAGE_DELETE_PROCEDURE_SUCCESS, procedureDeleted)); + } + + /** + * Creates and returns a {@code Client} with their {@code Procedure} updated by + * {@code updatedProcedureList}. + */ + private Client updateClientProcedures(Client clientToEdit, + List updatedProcedureList) { + assert clientToEdit != null && updatedProcedureList != null; + + Name updatedName = clientToEdit.getName(); + Phone updatedPhone = clientToEdit.getPhone(); + Email updatedEmail = clientToEdit.getEmail(); + Plan updatedPlan = clientToEdit.getPlan(); + Address updatedAddress = clientToEdit.getAddress(); + Set updatedTags = clientToEdit.getTags(); + List updatedProcedures = new ArrayList<>(updatedProcedureList); + return new Client(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedPlan, + updatedTags, updatedProcedures); + } + + /** + * Creates and returns a list of {@code Procedure} with the intended {@code Procedure} at + * {@code procedureIndex} deleted. + */ + private List deleteProcedureFromList(List procedureList, Index procedureIndex) + throws CommandException { + if (procedureIndex.getOneBased() > procedureList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PROCEDURE_DISPLAYED_INDEX); + } + if (procedureIndex.getOneBased() <= 0) { + throw new CommandException(Messages.MESSAGE_INVALID_PROCEDURE_NON_POSITIVE_INDEX); + } + + List updatedProcedureList = new ArrayList<>(procedureList); + updatedProcedureList.remove(procedureIndex.getZeroBased()); + return updatedProcedureList; + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof DeleteProcCommand)) { + return false; + } + + // state check + DeleteProcCommand e = (DeleteProcCommand) other; + return clientIndex.equals(e.clientIndex) + && procedureIndex.equals(e.procedureIndex); + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java index 7e36114902f..e250794a0e1 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/address/logic/commands/EditCommand.java @@ -5,9 +5,11 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PLAN; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_CLIENTS; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -19,87 +21,94 @@ import seedu.address.commons.util.CollectionUtil; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; +import seedu.address.model.client.Address; +import seedu.address.model.client.Client; +import seedu.address.model.client.Email; +import seedu.address.model.client.Name; +import seedu.address.model.client.Phone; +import seedu.address.model.client.Plan; +import seedu.address.model.procedure.Procedure; import seedu.address.model.tag.Tag; /** - * Edits the details of an existing person in the address book. + * Edits the details of an existing client in the address book. */ public class EditCommand extends Command { public static final String COMMAND_WORD = "edit"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified " - + "by the index number used in the displayed person list. " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the client identified " + + "by the index number used in the displayed client list. " + "Existing values will be overwritten by the input values.\n" + "Parameters: INDEX (must be a positive integer) " + "[" + PREFIX_NAME + "NAME] " + "[" + PREFIX_PHONE + "PHONE] " + "[" + PREFIX_EMAIL + "EMAIL] " + "[" + PREFIX_ADDRESS + "ADDRESS] " + + "[" + PREFIX_PLAN + "PLAN] " + "[" + PREFIX_TAG + "TAG]...\n" + "Example: " + COMMAND_WORD + " 1 " + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; + + PREFIX_EMAIL + "apple@example.com"; - public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; + public static final String MESSAGE_EDIT_CLIENT_SUCCESS = "Edited Client: %1$s"; public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book."; + public static final String MESSAGE_DUPLICATE_CLIENT = "A client with a duplicate address" + + " already exists in the address book."; private final Index index; - private final EditPersonDescriptor editPersonDescriptor; + private final EditClientDescriptor editClientDescriptor; /** - * @param index of the person in the filtered person list to edit - * @param editPersonDescriptor details to edit the person with + * @param index of the client in the filtered client list to edit + * @param editClientDescriptor details to edit the client with */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { + public EditCommand(Index index, EditClientDescriptor editClientDescriptor) { requireNonNull(index); - requireNonNull(editPersonDescriptor); + requireNonNull(editClientDescriptor); this.index = index; - this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); + this.editClientDescriptor = new EditClientDescriptor(editClientDescriptor); } @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); + List lastShownList = model.getFilteredClientList(); if (index.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + throw new CommandException(Messages.MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX); } - Person personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); + Client clientToEdit = lastShownList.get(index.getZeroBased()); + Client editedClient = createEditedClient(clientToEdit, editClientDescriptor); - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); + if (!clientToEdit.isSameClient(editedClient) && model.hasClient(editedClient)) { + throw new CommandException(MESSAGE_DUPLICATE_CLIENT); } - model.setPerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); + model.setClient(clientToEdit, editedClient); + model.updateFilteredClientList(PREDICATE_SHOW_ALL_CLIENTS); + return new CommandResult(String.format(MESSAGE_EDIT_CLIENT_SUCCESS, editedClient)); } /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. + * Creates and returns a {@code Client} with the details of {@code clientToEdit} + * edited with {@code editClientDescriptor}. */ - private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { - assert personToEdit != null; - - Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); - Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); - Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); - Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); - Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); + private static Client createEditedClient(Client clientToEdit, EditClientDescriptor editClientDescriptor) { + assert clientToEdit != null; + + Name updatedName = editClientDescriptor.getName().orElse(clientToEdit.getName()); + Phone updatedPhone = editClientDescriptor.getPhone().orElse(clientToEdit.getPhone()); + Email updatedEmail = editClientDescriptor.getEmail().orElse(clientToEdit.getEmail()); + Address updatedAddress = editClientDescriptor.getAddress().orElse(clientToEdit.getAddress()); + Plan updatedPlan = editClientDescriptor.getPlan().orElse(clientToEdit.getPlan()); + Set updatedTags = editClientDescriptor.getTags().orElse(clientToEdit.getTags()); + List updatedProcedures = clientToEdit.getProcedures(); + + return new Client(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedPlan, + updatedTags, updatedProcedures); } @Override @@ -117,39 +126,43 @@ public boolean equals(Object other) { // state check EditCommand e = (EditCommand) other; return index.equals(e.index) - && editPersonDescriptor.equals(e.editPersonDescriptor); + && editClientDescriptor.equals(e.editClientDescriptor); } /** - * Stores the details to edit the person with. Each non-empty field value will replace the - * corresponding field value of the person. + * Stores the details to edit the client with. Each non-empty field value will replace the + * corresponding field value of the client. */ - public static class EditPersonDescriptor { + public static class EditClientDescriptor { private Name name; private Phone phone; private Email email; private Address address; + private Plan plan; private Set tags; + private List procedures; - public EditPersonDescriptor() {} + public EditClientDescriptor() {} /** * Copy constructor. * A defensive copy of {@code tags} is used internally. */ - public EditPersonDescriptor(EditPersonDescriptor toCopy) { + public EditClientDescriptor(EditClientDescriptor toCopy) { setName(toCopy.name); setPhone(toCopy.phone); setEmail(toCopy.email); setAddress(toCopy.address); + setPlan(toCopy.plan); setTags(toCopy.tags); + setProcedures(toCopy.procedures); } /** * Returns true if at least one field is edited. */ public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); + return CollectionUtil.isAnyNonNull(name, phone, email, address, plan, tags, procedures); } public void setName(Name name) { @@ -184,6 +197,14 @@ public Optional
getAddress() { return Optional.ofNullable(address); } + public void setPlan(Plan plan) { + this.plan = plan; + } + + public Optional getPlan() { + return Optional.ofNullable(plan); + } + /** * Sets {@code tags} to this object's {@code tags}. * A defensive copy of {@code tags} is used internally. @@ -201,6 +222,23 @@ public Optional> getTags() { return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); } + /** + * Sets {@code Procedures} to this object's {@code Procedures}. + * A defensive copy of {@code Procedures} is used internally. + */ + public void setProcedures(List procedures) { + this.procedures = (procedures != null) ? new ArrayList<>(procedures) : null; + } + + /** + * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * Returns {@code Optional#empty()} if {@code tags} is null. + */ + public Optional> getProcedures() { + return (procedures != null) ? Optional.of(Collections.unmodifiableList(procedures)) : Optional.empty(); + } + @Override public boolean equals(Object other) { // short circuit if same object @@ -209,17 +247,18 @@ public boolean equals(Object other) { } // instanceof handles nulls - if (!(other instanceof EditPersonDescriptor)) { + if (!(other instanceof EditClientDescriptor)) { return false; } // state check - EditPersonDescriptor e = (EditPersonDescriptor) other; + EditClientDescriptor e = (EditClientDescriptor) other; return getName().equals(e.getName()) && getPhone().equals(e.getPhone()) && getEmail().equals(e.getEmail()) && getAddress().equals(e.getAddress()) + && getPlan().equals(e.getPlan()) && getTags().equals(e.getTags()); } } diff --git a/src/main/java/seedu/address/logic/commands/EditProcCommand.java b/src/main/java/seedu/address/logic/commands/EditProcCommand.java new file mode 100644 index 00000000000..0d12580f32f --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/EditProcCommand.java @@ -0,0 +1,277 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COST; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_INFORMATION; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_CLIENTS; +import static seedu.address.model.Model.PREDICATE_SHOW_CLIENT_PROCEDURES; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.client.Address; +import seedu.address.model.client.Client; +import seedu.address.model.client.Email; +import seedu.address.model.client.Name; +import seedu.address.model.client.Phone; +import seedu.address.model.client.Plan; +import seedu.address.model.procedure.Completion; +import seedu.address.model.procedure.Cost; +import seedu.address.model.procedure.Date; +import seedu.address.model.procedure.Information; +import seedu.address.model.procedure.Procedure; +import seedu.address.model.tag.Tag; + +/** + * Edits the details of an existing Procedure belonging to an existing Client in the address book. + */ +public class EditProcCommand extends Command { + public static final String COMMAND_WORD = "editProc"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the Procedure " + + "identified by the index number used in the Client Panel and the index number used " + + "in the Procedure Panel.\n" + + "Existing values will be overwritten by the input values.\n" + + "Parameters: CLIENT_INDEX (must be a positive integer) " + + "PROCEDURE_INDEX (must be a positive integer) " + + "[" + PREFIX_INFORMATION + "INFORMATION] " + + "[" + PREFIX_DATE + "DATE] " + + "[" + PREFIX_COST + "COST] \n" + + "Example: " + COMMAND_WORD + " 1 2 " + + PREFIX_INFORMATION + "Install modem " + + PREFIX_DATE + "31/03/2022 09:50 " + + PREFIX_COST + "67.25 "; + + + public static final String MESSAGE_EDIT_PROCEDURE_SUCCESS = "Edited Procedure: %1$s. " + + "\n From client: %2$s, at %3$s."; + public static final String MESSAGE_INVALID_EDIT_PROCEDURE_DUPLICATED = "This Client already has this " + + "Procedure.\nEnsure that the edited field(s) of the Procedure does not share the same information, " + + "date, time, and cost with itself, or another Procedure that belongs to this Client."; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + + private final Index clientIndex; + private final Index procedureIndex; + private final EditProcedureDescriptor editProcedureDescriptor; + + /** + * @param clientIndex of the Client in the filtered client list to edit + * @param procedureIndex of the Procedure in the Client's procedure list to edit + * @param editProcedureDescriptor details to edit the Procedure with + */ + public EditProcCommand(Index clientIndex, Index procedureIndex, + EditProcedureDescriptor editProcedureDescriptor) { + requireNonNull(clientIndex); + requireNonNull(procedureIndex); + requireNonNull(editProcedureDescriptor); + + this.clientIndex = clientIndex; + this.procedureIndex = procedureIndex; + this.editProcedureDescriptor = new EditProcedureDescriptor(editProcedureDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredClientList(); + + if (clientIndex.getOneBased() > lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX); + } + + Client clientToEdit = lastShownList.get(clientIndex.getZeroBased()); + List procedureList = clientToEdit.getProcedures(); + + if (procedureIndex.getOneBased() > procedureList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PROCEDURE_DISPLAYED_INDEX); //Customise later + } + + Procedure procedureToEdit = clientToEdit.getProcedures().get(procedureIndex.getZeroBased()); + Procedure updatedProcedure = createEditedProcedure(procedureToEdit, editProcedureDescriptor); + List updatedProcedureList = updateProcedureList(clientToEdit.getProcedures(), + updatedProcedure, procedureIndex); + Client editedClient = editClientProcedures(clientToEdit, updatedProcedureList); + + model.setClient(clientToEdit, editedClient); + model.updateFilteredClientList(PREDICATE_SHOW_ALL_CLIENTS); + model.updateFilteredProcedureList(editedClient, PREDICATE_SHOW_CLIENT_PROCEDURES); + return new CommandResult(String.format(MESSAGE_EDIT_PROCEDURE_SUCCESS, + updatedProcedure, editedClient.getName(), editedClient.getAddress())); + } + + /** + * Creates and returns a {@code Client} with the procedures of {@code clientToEdit} + * edited with {@code newProcedureList}. + */ + private static Client editClientProcedures(Client clientToEdit, List newProcedureList) { + assert clientToEdit != null; + + Name clientName = clientToEdit.getName(); + Phone clientPhone = clientToEdit.getPhone(); + Email clientEmail = clientToEdit.getEmail(); + Address clientAddress = clientToEdit.getAddress(); + Plan clientPlan = clientToEdit.getPlan(); + Set clientTags = clientToEdit.getTags(); + List updatedProcedures = new ArrayList<>(newProcedureList); + + return new Client(clientName, clientPhone, clientEmail, clientAddress, clientPlan, clientTags, + updatedProcedures); + } + + /** + * Checks if new {@code Procedure} already has a duplicate in the {@code procedureList}. + */ + private static boolean hasDuplicateProcInList(List procedureList, Procedure newProcedure) { + for (Procedure procedure : procedureList) { + if (newProcedure.isProcedureDuplicate(procedure)) { + return true; + } + } + return false; + } + + /** + * Creates and returns a list of {@code Procedure} with the {@code Procedure} at + * {@code procedureIndex} in the {@code procedureList} updated with {@code edittedProcedure}. + */ + private static List updateProcedureList(List procedureList, + Procedure edittedProcedure, Index procedureIndex) throws CommandException { + assert procedureList.size() > 0; + assert edittedProcedure != null; + assert procedureIndex != null; + + if (procedureIndex.getOneBased() > procedureList.size()) { + throw new CommandException("Procedure index input exceeds the amount of procedure in the Client"); + } // do for negative + + if (hasDuplicateProcInList(procedureList, edittedProcedure)) { + throw new CommandException(MESSAGE_INVALID_EDIT_PROCEDURE_DUPLICATED); + } + + List newProcedureList = new ArrayList<>(procedureList); + newProcedureList.set(procedureIndex.getZeroBased(), edittedProcedure); + Comparator mapComparator = (Procedure m1, Procedure m2) -> m1.getDate() + .compareTo(m2.getDate()); + Collections.sort(newProcedureList, mapComparator); + + return newProcedureList; + } + + /** + * Creates and returns a {@code Procedure} with the details of {@code procedureToEdit} + * edited with {@code editProcedureDescriptor}. + */ + private static Procedure createEditedProcedure(Procedure procedureToEdit, + EditProcedureDescriptor editProcedureDescriptor) { + assert procedureToEdit != null; + Information updatedInfo = editProcedureDescriptor.getInfo() + .orElse(procedureToEdit.getInfo()); + Date updatedDate = editProcedureDescriptor.getDate() + .orElse(procedureToEdit.getDate()); + Cost updatedCost = editProcedureDescriptor.getCost() + .orElse(procedureToEdit.getCost()); + Completion hasCompleted = (procedureToEdit.getHasCompleted()); + + return new Procedure(updatedInfo, updatedDate, updatedCost, hasCompleted); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditProcCommand)) { + return false; + } + + // state check + EditProcCommand e = (EditProcCommand) other; + return clientIndex.equals(e.clientIndex) + && procedureIndex.equals(e.procedureIndex) + && editProcedureDescriptor.equals(e.editProcedureDescriptor); + } + + /** + * Stores the details to edit the {@code Procedure} with. Each non-empty field value will replace the + * corresponding field value of the client. + */ + public static class EditProcedureDescriptor { + private Information info; + private Cost cost; + private Date date; + + public EditProcedureDescriptor() {} + + /** + * Copy constructor. + */ + public EditProcedureDescriptor(EditProcedureDescriptor toCopy) { + setInfo(toCopy.info); + setDate(toCopy.date); + setCost(toCopy.cost); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldsEdited() { + return CollectionUtil.isAnyNonNull(info, cost, date); + } + + public void setInfo(Information info) { + this.info = info; + } + + public Optional getInfo() { + return Optional.ofNullable(info); + } + + public void setDate(Date date) { + this.date = date; + } + + public Optional getDate() { + return Optional.ofNullable(date); + } + + public void setCost(Cost cost) { + this.cost = cost; + } + + public Optional getCost() { + return Optional.ofNullable(cost); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditProcedureDescriptor)) { + return false; + } + + // state check + EditProcedureDescriptor e = (EditProcedureDescriptor) other; + return getInfo().equals(e.getInfo()) + && getCost().equals(e.getCost()) + && getDate().equals(e.getDate()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java index 3dd85a8ba90..0f3e7c80df9 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java @@ -11,9 +11,16 @@ public class ExitCommand extends Command { public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Exits the program. \n" + + "Example: exit"; + @Override public CommandResult execute(Model model) { return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); } + @Override + public boolean equals(Object other) { + return other instanceof ExitCommand; + } } diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java index d6b19b0a0de..ea641ef612b 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/seedu/address/logic/commands/FindCommand.java @@ -2,22 +2,26 @@ import static java.util.Objects.requireNonNull; +import java.util.ArrayList; + import seedu.address.commons.core.Messages; import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.client.NameContainsKeywordsPredicate; +import seedu.address.model.procedure.Procedure; + /** - * Finds and lists all persons in address book whose name contains any of the argument keywords. + * Finds and lists all clients in address book whose name contains any of the argument keywords. * Keyword matching is case insensitive. */ public class FindCommand extends Command { public static final String COMMAND_WORD = "find"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all clients whose names contain any of " + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" - + "Example: " + COMMAND_WORD + " alice bob charlie"; + + "Example: " + COMMAND_WORD + " burger bags mcdonald's"; private final NameContainsKeywordsPredicate predicate; @@ -28,9 +32,15 @@ public FindCommand(NameContainsKeywordsPredicate predicate) { @Override public CommandResult execute(Model model) { requireNonNull(model); - model.updateFilteredPersonList(predicate); + model.updateFilteredClientList(predicate); + if (model.getFilteredClientList().size() > 0) { + model.updateFilteredProcedureList(model.getFilteredClientList().get(0), + Model.PREDICATE_SHOW_CLIENT_PROCEDURES); + } else { + model.setProcedures(new ArrayList()); + } return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); + String.format(Messages.MESSAGE_CLIENTS_LISTED_OVERVIEW, model.getFilteredClientList().size())); } @Override diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java index bf824f91bd0..4a91cf09d11 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java @@ -7,6 +7,7 @@ */ public class HelpCommand extends Command { + public static final String COMMAND_WORD = "help"; public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions.\n" @@ -18,4 +19,9 @@ public class HelpCommand extends Command { public CommandResult execute(Model model) { return new CommandResult(SHOWING_HELP_MESSAGE, true, false); } + + @Override + public boolean equals(Object other) { + return other instanceof HelpCommand; + } } diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java index 84be6ad2596..cf90471cb11 100644 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ b/src/main/java/seedu/address/logic/commands/ListCommand.java @@ -1,24 +1,32 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_CLIENTS; import seedu.address.model.Model; /** - * Lists all persons in the address book to the user. + * Lists all clients in the address book to the user. */ public class ListCommand extends Command { public static final String COMMAND_WORD = "list"; - public static final String MESSAGE_SUCCESS = "Listed all persons"; + public static final String MESSAGE_SUCCESS = "Listed all clients"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists all the clients in the address book. \n" + + "Example: list"; @Override public CommandResult execute(Model model) { requireNonNull(model); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + model.updateFilteredClientList(PREDICATE_SHOW_ALL_CLIENTS); return new CommandResult(MESSAGE_SUCCESS); } + + @Override + public boolean equals(Object other) { + return other instanceof ListCommand; + } } diff --git a/src/main/java/seedu/address/logic/commands/ListProcCommand.java b/src/main/java/seedu/address/logic/commands/ListProcCommand.java new file mode 100644 index 00000000000..1ee1a4cb57c --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ListProcCommand.java @@ -0,0 +1,62 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_CLIENT_PROCEDURES; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.client.Client; + +public class ListProcCommand extends Command { + + public static final String COMMAND_WORD = "listProc"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists the procedures of a client in the index. \n" + + "Parameters: INDEX (must be a positive integer) \n" + + "Example: listProc 1"; + + public static final String MESSAGE_SUCCESS = "Procedures successfully loaded."; + + public static final String MESSAGE_FAIL = "No Procedures to load!"; + + private final Index targetIndex; + + /** + * Creates an ListProcCommand to add the specified {@code Client} + */ + public ListProcCommand(Index targetIndex) { + requireNonNull(targetIndex); + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredClientList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX); + } + Client clientToShow = lastShownList.get(targetIndex.getZeroBased()); + if (clientToShow.getProcedures().isEmpty()) { + model.setProcedures(clientToShow.getProcedures()); + model.updateFilteredProcedureList(clientToShow, PREDICATE_SHOW_CLIENT_PROCEDURES); + return new CommandResult(MESSAGE_FAIL); + } else { + model.setProcedures(clientToShow.getProcedures()); + model.updateFilteredProcedureList(clientToShow, PREDICATE_SHOW_CLIENT_PROCEDURES); + return new CommandResult(MESSAGE_SUCCESS); + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ListProcCommand // instanceof handles nulls + && targetIndex.equals(((ListProcCommand) other).targetIndex)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ListProcOnCommand.java b/src/main/java/seedu/address/logic/commands/ListProcOnCommand.java new file mode 100644 index 00000000000..72c2dd6eb75 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ListProcOnCommand.java @@ -0,0 +1,95 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import javafx.util.Pair; +import seedu.address.commons.core.datewithouttime.DateWithoutTime; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.client.Client; +import seedu.address.model.procedure.Procedure; + +/** + * Get all Procedures scheduled on a givenDate + */ +public class ListProcOnCommand extends Command { + + public static final String COMMAND_WORD = "listProcOn"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Lists all Procedures to be done on a given date\n" + + "Parameters: date (must be a valid date)\n" + + "Example: " + COMMAND_WORD + " 02/02/2022"; + + public static final String MESSAGE_LIST_PROC_SUCCESS = "Listing Procedures on requested date:\n%1$s"; + public static final String MESSAGE_NO_PROCEDURES = "Can't find any Procedure on requested date!"; + + private final DateWithoutTime targetDate; + + public ListProcOnCommand(DateWithoutTime date) { + this.targetDate = date; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + List lastShownList = model.getFilteredClientList(); + List> procedureClientPairList = new ArrayList<>(); + + for (int i = 0; i < lastShownList.size(); i++) { + Client currentClient = lastShownList.get(i); + List clientProceduresOnDate = currentClient.getProcsOnDate(targetDate); + clientProceduresOnDate.stream().map(Procedure -> new Pair(Procedure, currentClient)) + .forEach(procedureClientPairList::add); + } + + Collections.sort(procedureClientPairList, new Comparator>() { + @Override + public int compare(Pair p1, Pair p2) { + return p1.getKey().compareTo(p2.getKey()); + } + }); + + String resultString = composeResultString(procedureClientPairList); + + if (procedureClientPairList.isEmpty()) { + return new CommandResult(MESSAGE_NO_PROCEDURES); + } else { + return new CommandResult(String.format(MESSAGE_LIST_PROC_SUCCESS, resultString)); + } + } + + private String composeResultString(List> procedureClientPairList) { + StringBuilder proceduresToStringBuilder = new StringBuilder(); + for (int i = 0; i < procedureClientPairList.size(); i++) { + int index = i + 1; + proceduresToStringBuilder.append(index + ". " + procedureClientPairList.get(i).getKey().toString() + "\n"); + proceduresToStringBuilder.append(procedureClientPairList.get(i).getValue().getName().toString() + + ", located at " + procedureClientPairList.get(i).getValue().getAddress().toString() + "\n"); + } + return proceduresToStringBuilder.toString(); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ListProcOnCommand)) { + return false; + } + + // state check + ListProcOnCommand otherCommand = (ListProcOnCommand) other; + return targetDate.equals(otherCommand.targetDate); + } +} diff --git a/src/main/java/seedu/address/logic/commands/MarkCommand.java b/src/main/java/seedu/address/logic/commands/MarkCommand.java new file mode 100644 index 00000000000..fa4dbdf3152 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/MarkCommand.java @@ -0,0 +1,78 @@ +package seedu.address.logic.commands; +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.client.Client; +import seedu.address.model.procedure.Completion; +import seedu.address.model.procedure.Procedure; + +public class MarkCommand extends Command { + + public static final String COMMAND_WORD = "mark"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Marks the procedure in the client as complete. \n" + + "Parameters: CLIENT_INDEX, PROCEDURE_INDEX (must be both positive integers) \n" + + "Example: mark 1 1"; + + public static final String MESSAGE_SUCCESS = "Procedure successfully marked as complete."; + + public static final String MESSAGE_PROCEDURE_NOT_DISPLAYED = "No procedure list is retrieved yet! You can call " + + "listProc command to select the list."; + + private final Index targetClientIndex; + private final Index targetProcedureIndex; + + /** + * Creates an MarkCommand for a {@code Procedure} in a {@code Client} + */ + public MarkCommand(Index targetClientIndex, Index targetProcedureIndex) { + requireNonNull(targetClientIndex); + requireNonNull(targetProcedureIndex); + this.targetClientIndex = targetClientIndex; + this.targetProcedureIndex = targetProcedureIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredClientList(); + + if (targetClientIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX); + } + + Client clientToShow = lastShownList.get(targetClientIndex.getZeroBased()); + List procedureList = clientToShow.getProcedures(); + if (targetProcedureIndex.getZeroBased() >= procedureList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PROCEDURE_DISPLAYED_INDEX); + } + + if (model.getFilteredProcedureList().isEmpty()) { + throw new CommandException(MESSAGE_PROCEDURE_NOT_DISPLAYED); + } + Procedure targetProcedure = procedureList.get(targetProcedureIndex.getZeroBased()); + targetProcedure.setHasCompleted(new Completion("true")); + + return new CommandResult(MESSAGE_SUCCESS); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (other instanceof MarkCommand) { + MarkCommand otherCommand = (MarkCommand) other; + return targetClientIndex.equals(otherCommand.targetClientIndex) + && targetProcedureIndex.equals(otherCommand.targetProcedureIndex); + } else { + return false; + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/UnmarkCommand.java b/src/main/java/seedu/address/logic/commands/UnmarkCommand.java new file mode 100644 index 00000000000..49896c5fac0 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/UnmarkCommand.java @@ -0,0 +1,73 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.client.Client; +import seedu.address.model.procedure.Completion; +import seedu.address.model.procedure.Procedure; + +public class UnmarkCommand extends Command { + + public static final String COMMAND_WORD = "unmark"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Mark the procedure in the client as incomplete. \n" + + "Parameters: CLIENT_INDEX, PROCEDURE_INDEX (must be both positive integers) \n" + + "Example: unmark 1 1"; + + public static final String MESSAGE_SUCCESS = "Procedure successfully unmarked."; + + private final Index targetClientIndex; + private final Index targetProcedureIndex; + + /** + * Creates an UnmarkCommand for a {@code Procedure} in a {@code Client} + */ + public UnmarkCommand(Index targetClientIndex, Index targetProcedureIndex) { + requireNonNull(targetClientIndex); + requireNonNull(targetProcedureIndex); + this.targetClientIndex = targetClientIndex; + this.targetProcedureIndex = targetProcedureIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredClientList(); + + if (targetClientIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX); + } + + Client clientToShow = lastShownList.get(targetClientIndex.getZeroBased()); + List procedureList = clientToShow.getProcedures(); + if (targetProcedureIndex.getZeroBased() >= procedureList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PROCEDURE_DISPLAYED_INDEX); + } + + Procedure targetProcedure = procedureList.get(targetProcedureIndex.getZeroBased()); + targetProcedure.setHasCompleted(new Completion("false")); + + return new CommandResult(MESSAGE_SUCCESS); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (other instanceof UnmarkCommand) { + UnmarkCommand otherCommand = (UnmarkCommand) other; + return targetClientIndex.equals(otherCommand.targetClientIndex) + && targetProcedureIndex.equals(otherCommand.targetProcedureIndex); + } else { + return false; + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddClientCommandParser.java similarity index 63% rename from src/main/java/seedu/address/logic/parser/AddCommandParser.java rename to src/main/java/seedu/address/logic/parser/AddClientCommandParser.java index 3b8bfa035e8..3ef66af1af4 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddClientCommandParser.java @@ -5,48 +5,52 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PLAN; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import java.util.Set; import java.util.stream.Stream; -import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.AddClientCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; +import seedu.address.model.client.Address; +import seedu.address.model.client.Client; +import seedu.address.model.client.Email; +import seedu.address.model.client.Name; +import seedu.address.model.client.Phone; +import seedu.address.model.client.Plan; import seedu.address.model.tag.Tag; /** - * Parses input arguments and creates a new AddCommand object + * Parses input arguments and creates a new AddClientCommand object */ -public class AddCommandParser implements Parser { +public class AddClientCommandParser implements Parser { /** - * Parses the given {@code String} of arguments in the context of the AddCommand - * and returns an AddCommand object for execution. + * Parses the given {@code String} of arguments in the context of the AddClientCommand + * and returns an AddClientCommand object for execution. * @throws ParseException if the user input does not conform the expected format */ - public AddCommand parse(String args) throws ParseException { + public AddClientCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_PLAN, PREFIX_TAG); - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PLAN, PREFIX_PHONE, PREFIX_EMAIL) || !argMultimap.getPreamble().isEmpty()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddClientCommand.MESSAGE_USAGE)); } Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); + Plan plan = ParserUtil.parsePlan(argMultimap.getValue(PREFIX_PLAN).get()); Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); - Person person = new Person(name, phone, email, address, tagList); + Client client = new Client(name, phone, email, address, plan, tagList); - return new AddCommand(person); + return new AddClientCommand(client); } /** diff --git a/src/main/java/seedu/address/logic/parser/AddProcCommandParser.java b/src/main/java/seedu/address/logic/parser/AddProcCommandParser.java new file mode 100644 index 00000000000..cb23fda9a46 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddProcCommandParser.java @@ -0,0 +1,65 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COST; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_INFORMATION; + +import java.util.stream.Stream; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.AddProcCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.procedure.Completion; +import seedu.address.model.procedure.Cost; +import seedu.address.model.procedure.Date; +import seedu.address.model.procedure.Information; +import seedu.address.model.procedure.Procedure; + + +/** + * Parses input arguments and creates a new AddProcCommand object + */ +public class AddProcCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddProcCommand + * and returns an AddProcCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddProcCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_INFORMATION, PREFIX_COST, PREFIX_DATE); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddProcCommand.MESSAGE_USAGE), pe); + } + + if (!arePrefixesPresent(argMultimap, PREFIX_INFORMATION, PREFIX_COST, PREFIX_DATE)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddProcCommand.MESSAGE_USAGE)); + } + + Information information = ParserUtil.parseInformation(argMultimap.getValue(PREFIX_INFORMATION).get()); + Cost cost = ParserUtil.parseCost(argMultimap.getValue(PREFIX_COST).get()); + Date date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get()); + // New Procedure is not completed by default + Completion completion = new Completion("false"); + + Procedure procedure = new Procedure(information, date, cost, completion); + + return new AddProcCommand(index, procedure); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index 1e466792b46..9bf62d91dae 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -6,15 +6,23 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.AddClientCommand; +import seedu.address.logic.commands.AddProcCommand; +import seedu.address.logic.commands.CalculateCommand; import seedu.address.logic.commands.ClearCommand; import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.DeleteCommand; +import seedu.address.logic.commands.DeleteClientCommand; +import seedu.address.logic.commands.DeleteProcCommand; import seedu.address.logic.commands.EditCommand; +import seedu.address.logic.commands.EditProcCommand; import seedu.address.logic.commands.ExitCommand; import seedu.address.logic.commands.FindCommand; import seedu.address.logic.commands.HelpCommand; import seedu.address.logic.commands.ListCommand; +import seedu.address.logic.commands.ListProcCommand; +import seedu.address.logic.commands.ListProcOnCommand; +import seedu.address.logic.commands.MarkCommand; +import seedu.address.logic.commands.UnmarkCommand; import seedu.address.logic.parser.exceptions.ParseException; /** @@ -44,29 +52,53 @@ public Command parseCommand(String userInput) throws ParseException { final String arguments = matcher.group("arguments"); switch (commandWord) { - case AddCommand.COMMAND_WORD: - return new AddCommandParser().parse(arguments); + case AddClientCommand.COMMAND_WORD: + return new AddClientCommandParser().parse(arguments); + + case AddProcCommand.COMMAND_WORD: + return new AddProcCommandParser().parse(arguments); case EditCommand.COMMAND_WORD: return new EditCommandParser().parse(arguments); - case DeleteCommand.COMMAND_WORD: - return new DeleteCommandParser().parse(arguments); + case DeleteClientCommand.COMMAND_WORD: + return new DeleteClientCommandParser().parse(arguments); + + case EditProcCommand.COMMAND_WORD: + return new EditProcCommandParser().parse(arguments); + + case DeleteProcCommand.COMMAND_WORD: + return new DeleteProcCommandParser().parse(arguments); case ClearCommand.COMMAND_WORD: - return new ClearCommand(); + return new ClearCommandParser().parse(arguments); case FindCommand.COMMAND_WORD: return new FindCommandParser().parse(arguments); case ListCommand.COMMAND_WORD: - return new ListCommand(); + return new ListCommandParser().parse(arguments); case ExitCommand.COMMAND_WORD: - return new ExitCommand(); + return new ExitCommandParser().parse(arguments); case HelpCommand.COMMAND_WORD: - return new HelpCommand(); + return new HelpCommandParser().parse(arguments); + + case ListProcCommand.COMMAND_WORD: + return new ListProcCommandParser().parse(arguments); + + case MarkCommand.COMMAND_WORD: + return new MarkCommandParser().parse(arguments); + + case UnmarkCommand.COMMAND_WORD: + return new UnmarkCommandParser().parse(arguments); + + case ListProcOnCommand.COMMAND_WORD: + return new ListProcOnCommandParser().parse(arguments); + + case CalculateCommand.COMMAND_WORD: + return new CalculateCommandParser().parse(arguments); default: throw new ParseException(MESSAGE_UNKNOWN_COMMAND); diff --git a/src/main/java/seedu/address/logic/parser/CalculateCommandParser.java b/src/main/java/seedu/address/logic/parser/CalculateCommandParser.java new file mode 100644 index 00000000000..925b1bfde92 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/CalculateCommandParser.java @@ -0,0 +1,25 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.datewithouttime.DateWithoutTime; +import seedu.address.logic.commands.CalculateCommand; +import seedu.address.logic.parser.exceptions.ParseException; + + +public class CalculateCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the CalculateCommand + * and returns a CalculateCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public CalculateCommand parse(String args) throws ParseException { + try { + DateWithoutTime date = ParserUtil.parseDateWithoutTime(args); + return new CalculateCommand(date); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DateWithoutTime.MESSAGE_CONSTRAINTS), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/ClearCommandParser.java b/src/main/java/seedu/address/logic/parser/ClearCommandParser.java new file mode 100644 index 00000000000..2c979855d66 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ClearCommandParser.java @@ -0,0 +1,18 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.logic.commands.ClearCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +public class ClearCommandParser implements Parser { + @Override + public ClearCommand parse(String args) throws ParseException { + if (args.trim().isEmpty()) { + return new ClearCommand(); + } else { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ClearCommand.MESSAGE_USAGE)); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index 75b1a9bf119..169785aaad5 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -5,11 +5,17 @@ */ public class CliSyntax { - /* Prefix definitions */ + /* Prefix definitions for Client */ public static final Prefix PREFIX_NAME = new Prefix("n/"); public static final Prefix PREFIX_PHONE = new Prefix("p/"); public static final Prefix PREFIX_EMAIL = new Prefix("e/"); public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); + public static final Prefix PREFIX_PLAN = new Prefix("l/"); public static final Prefix PREFIX_TAG = new Prefix("t/"); + /* Prefix definitions for Procedure */ + public static final Prefix PREFIX_INFORMATION = new Prefix("i/"); + public static final Prefix PREFIX_COST = new Prefix("c/"); + public static final Prefix PREFIX_DATE = new Prefix("d/"); + } diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteClientCommandParser.java similarity index 57% rename from src/main/java/seedu/address/logic/parser/DeleteCommandParser.java rename to src/main/java/seedu/address/logic/parser/DeleteClientCommandParser.java index 522b93081cc..d93014aa679 100644 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/DeleteClientCommandParser.java @@ -3,26 +3,26 @@ import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.DeleteCommand; +import seedu.address.logic.commands.DeleteClientCommand; import seedu.address.logic.parser.exceptions.ParseException; /** - * Parses input arguments and creates a new DeleteCommand object + * Parses input arguments and creates a new DeleteClientCommand object */ -public class DeleteCommandParser implements Parser { +public class DeleteClientCommandParser implements Parser { /** - * Parses the given {@code String} of arguments in the context of the DeleteCommand - * and returns a DeleteCommand object for execution. + * Parses the given {@code String} of arguments in the context of the DeleteClientCommand + * and returns a DeleteClientCommand object for execution. * @throws ParseException if the user input does not conform the expected format */ - public DeleteCommand parse(String args) throws ParseException { + public DeleteClientCommand parse(String args) throws ParseException { try { Index index = ParserUtil.parseIndex(args); - return new DeleteCommand(index); + return new DeleteClientCommand(index); } catch (ParseException pe) { throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe); + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteClientCommand.MESSAGE_USAGE), pe); } } diff --git a/src/main/java/seedu/address/logic/parser/DeleteProcCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteProcCommandParser.java new file mode 100644 index 00000000000..0bcd3b3fc20 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/DeleteProcCommandParser.java @@ -0,0 +1,42 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.ArrayList; +import java.util.List; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.DeleteProcCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EditCommand object + */ +public class DeleteProcCommandParser implements Parser { + public static final int GET_CLIENT_INDEX = 0; + public static final int GET_PROCEDURE_INDEX = 1; + + /** + * Parses the given {@code String} of arguments in the context of the DeleteProcCommand + * and returns an EditCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteProcCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args); + List indexes = new ArrayList<>(); + try { + indexes.addAll(ParserUtil.parseIndexes(argMultimap.getPreamble())); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteProcCommand.MESSAGE_USAGE), pe); + } + if (indexes.size() != 2) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteProcCommand.MESSAGE_USAGE)); + } + + return new DeleteProcCommand(indexes.get(GET_CLIENT_INDEX), indexes.get(GET_PROCEDURE_INDEX)); + } +} diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java index 845644b7dea..f0d222b42a4 100644 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java @@ -6,6 +6,7 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PLAN; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import java.util.Collection; @@ -15,7 +16,7 @@ import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.address.logic.commands.EditCommand.EditClientDescriptor; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.tag.Tag; @@ -32,7 +33,8 @@ public class EditCommandParser implements Parser { public EditCommand parse(String args) throws ParseException { requireNonNull(args); ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_PLAN, PREFIX_TAG); Index index; @@ -42,26 +44,29 @@ public EditCommand parse(String args) throws ParseException { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); } - EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); + EditClientDescriptor editClientDescriptor = new EditClientDescriptor(); if (argMultimap.getValue(PREFIX_NAME).isPresent()) { - editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); + editClientDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); } if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { - editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); + editClientDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); } if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { - editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); + editClientDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); } if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { - editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); + editClientDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); } - parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); + if (argMultimap.getValue(PREFIX_PLAN).isPresent()) { + editClientDescriptor.setPlan(ParserUtil.parsePlan(argMultimap.getValue(PREFIX_PLAN).get())); + } + parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editClientDescriptor::setTags); - if (!editPersonDescriptor.isAnyFieldEdited()) { + if (!editClientDescriptor.isAnyFieldEdited()) { throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); } - return new EditCommand(index, editPersonDescriptor); + return new EditCommand(index, editClientDescriptor); } /** diff --git a/src/main/java/seedu/address/logic/parser/EditProcCommandParser.java b/src/main/java/seedu/address/logic/parser/EditProcCommandParser.java new file mode 100644 index 00000000000..6ede0e8f12a --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/EditProcCommandParser.java @@ -0,0 +1,69 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COST; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_INFORMATION; +import static seedu.address.logic.parser.DeleteProcCommandParser.GET_CLIENT_INDEX; +import static seedu.address.logic.parser.DeleteProcCommandParser.GET_PROCEDURE_INDEX; + +import java.util.ArrayList; +import java.util.List; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.EditCommand; +import seedu.address.logic.commands.EditProcCommand; +import seedu.address.logic.commands.EditProcCommand.EditProcedureDescriptor; +import seedu.address.logic.parser.exceptions.ParseException; + + +/** + * Parses input arguments and creates a new EditProcCommand object + */ +public class EditProcCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the EditProcCommand + * and returns an EditProcCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditProcCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_INFORMATION, PREFIX_DATE, PREFIX_COST); + + List indexes = new ArrayList<>(); + + try { + indexes.addAll(ParserUtil.parseIndexes(argMultimap.getPreamble())); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditProcCommand.MESSAGE_USAGE), pe); + } + + EditProcedureDescriptor editProcDescriptor = new EditProcedureDescriptor(); + if (argMultimap.getValue(PREFIX_INFORMATION).isPresent()) { + editProcDescriptor.setInfo( + ParserUtil.parseInformation(argMultimap.getValue(PREFIX_INFORMATION).get())); + } + if (argMultimap.getValue(PREFIX_DATE).isPresent()) { + editProcDescriptor.setDate(ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get())); + } + if (argMultimap.getValue(PREFIX_COST).isPresent()) { + editProcDescriptor.setCost(ParserUtil.parseCost(argMultimap.getValue(PREFIX_COST).get())); + } + + if (!editProcDescriptor.isAnyFieldsEdited()) { + throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); + } + + if (indexes.size() != 2) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditProcCommand.MESSAGE_USAGE)); + } + + return new EditProcCommand(indexes.get(GET_CLIENT_INDEX), + indexes.get(GET_PROCEDURE_INDEX), editProcDescriptor); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/ExitCommandParser.java b/src/main/java/seedu/address/logic/parser/ExitCommandParser.java new file mode 100644 index 00000000000..f3c23f58693 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ExitCommandParser.java @@ -0,0 +1,18 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.logic.commands.ExitCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +public class ExitCommandParser implements Parser { + @Override + public ExitCommand parse(String args) throws ParseException { + if (args.trim().isEmpty()) { + return new ExitCommand(); + } else { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ExitCommand.MESSAGE_USAGE)); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java index 4fb71f23103..31662371f4a 100644 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java @@ -6,7 +6,7 @@ import seedu.address.logic.commands.FindCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.client.NameContainsKeywordsPredicate; /** * Parses input arguments and creates a new FindCommand object diff --git a/src/main/java/seedu/address/logic/parser/HelpCommandParser.java b/src/main/java/seedu/address/logic/parser/HelpCommandParser.java new file mode 100644 index 00000000000..ad3fd656898 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/HelpCommandParser.java @@ -0,0 +1,18 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.logic.commands.HelpCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +public class HelpCommandParser implements Parser { + @Override + public HelpCommand parse(String args) throws ParseException { + if (args.trim().isEmpty()) { + return new HelpCommand(); + } else { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/ListCommandParser.java b/src/main/java/seedu/address/logic/parser/ListCommandParser.java new file mode 100644 index 00000000000..a14da1b536a --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ListCommandParser.java @@ -0,0 +1,18 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.logic.commands.ListCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +public class ListCommandParser implements Parser { + @Override + public ListCommand parse(String args) throws ParseException { + if (args.trim().isEmpty()) { + return new ListCommand(); + } else { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_USAGE)); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/ListProcCommandParser.java b/src/main/java/seedu/address/logic/parser/ListProcCommandParser.java new file mode 100644 index 00000000000..ad06a089915 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ListProcCommandParser.java @@ -0,0 +1,21 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.ListProcCommand; +import seedu.address.logic.parser.exceptions.ParseException; + + +public class ListProcCommandParser implements Parser { + @Override + public ListProcCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new ListProcCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListProcCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/ListProcOnCommandParser.java b/src/main/java/seedu/address/logic/parser/ListProcOnCommandParser.java new file mode 100644 index 00000000000..0d48f43cbbb --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ListProcOnCommandParser.java @@ -0,0 +1,24 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.datewithouttime.DateWithoutTime; +import seedu.address.logic.commands.ListProcOnCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +public class ListProcOnCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the ListProcOnCommand + * and returns a ListProcOnCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ListProcOnCommand parse(String args) throws ParseException { + try { + DateWithoutTime date = ParserUtil.parseDateWithoutTime(args); + return new ListProcOnCommand(date); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DateWithoutTime.MESSAGE_CONSTRAINTS), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/MarkCommandParser.java b/src/main/java/seedu/address/logic/parser/MarkCommandParser.java new file mode 100644 index 00000000000..3cc51eca1a0 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/MarkCommandParser.java @@ -0,0 +1,41 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.ArrayList; +import java.util.List; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.MarkCommand; +import seedu.address.logic.parser.exceptions.ParseException; + + +public class MarkCommandParser implements Parser { + public static final int GET_CLIENT_INDEX = 0; + public static final int GET_PROCEDURE_INDEX = 1; + + /** + * Parses the given {@code String} of arguments in the context of the MarkCommand + * and returns a MarkCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public MarkCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argumentMultimap = ArgumentTokenizer.tokenize(args); + List indexes = new ArrayList<>(); + try { + indexes.addAll(ParserUtil.parseIndexes(argumentMultimap.getPreamble())); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + MarkCommand.MESSAGE_USAGE), pe); + } + if (indexes.size() != 2) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + MarkCommand.MESSAGE_USAGE)); + } + + return new MarkCommand(indexes.get(GET_CLIENT_INDEX), indexes.get(GET_PROCEDURE_INDEX)); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index b117acb9c55..66b1a002411 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -2,17 +2,24 @@ import static java.util.Objects.requireNonNull; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; +import seedu.address.commons.core.datewithouttime.DateWithoutTime; import seedu.address.commons.core.index.Index; import seedu.address.commons.util.StringUtil; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; +import seedu.address.model.client.Address; +import seedu.address.model.client.Email; +import seedu.address.model.client.Name; +import seedu.address.model.client.Phone; +import seedu.address.model.client.Plan; +import seedu.address.model.procedure.Cost; +import seedu.address.model.procedure.Date; +import seedu.address.model.procedure.Information; import seedu.address.model.tag.Tag; /** @@ -35,6 +42,27 @@ public static Index parseIndex(String oneBasedIndex) throws ParseException { return Index.fromOneBased(Integer.parseInt(trimmedIndex)); } + /** + * Parses {@code oneBasedIndexes} into a list of {@code Index} and returns it. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException if the specified index is invalid (not non-zero unsigned integer). + */ + public static List parseIndexes(String oneBasedIndexes) throws ParseException { + String trimmedInput = oneBasedIndexes.trim(); + String[] listOfInputs = trimmedInput.split(" "); + List listofIndexes = new ArrayList<>(); + + for (String listOfInput : listOfInputs) { + if (!StringUtil.isNonZeroUnsignedInteger(listOfInput)) { + throw new ParseException(MESSAGE_INVALID_INDEX); + } + listOfInput = listOfInput.trim(); + listofIndexes.add(Index.fromOneBased(Integer.parseInt(listOfInput))); + } + + return listofIndexes; + } + /** * Parses a {@code String name} into a {@code Name}. * Leading and trailing whitespaces will be trimmed. @@ -80,6 +108,21 @@ public static Address parseAddress(String address) throws ParseException { return new Address(trimmedAddress); } + /** + * Parses a {@code String plan} into an {@code Plan}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code plan} is invalid. + */ + public static Plan parsePlan(String plan) throws ParseException { + requireNonNull(plan); + String trimmedPlan = plan.trim(); + if (!Plan.isValidPlan(trimmedPlan)) { + throw new ParseException(Plan.MESSAGE_CONSTRAINTS); + } + return new Plan(trimmedPlan); + } + /** * Parses a {@code String email} into an {@code Email}. * Leading and trailing whitespaces will be trimmed. @@ -121,4 +164,66 @@ public static Set parseTags(Collection tags) throws ParseException } return tagSet; } + + /** + * Parses a {@code String information} into an {@code Information}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code information} is invalid. + */ + public static Information parseInformation(String information) throws ParseException { + requireNonNull(information); + String trimmedInformation = information.trim(); + if (!Information.isValidInformation(trimmedInformation)) { + throw new ParseException(Information.MESSAGE_CONSTRAINTS); + } + return new Information(information); + } + + /** + * Parses a {@code String cost} into a {@code Cost}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code cost} is invalid. + */ + public static Cost parseCost(String cost) throws ParseException { + requireNonNull(cost); + String trimmedCost = cost.trim(); + if (!Cost.isValidCost(trimmedCost)) { + throw new ParseException(Cost.MESSAGE_CONSTRAINTS); + } + return new Cost(cost); + } + + /** + * Parses a {@code String date} into a {@code Date}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code date} is invalid. + */ + public static Date parseDate(String date) throws ParseException { + requireNonNull(date); + String trimmedDate = date.trim(); + if (!Date.isValidDate(trimmedDate)) { + throw new ParseException(Date.MESSAGE_CONSTRAINTS); + } + return new Date(date); + } + + /** + * Parses a {@code String date} into a {@code Date}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code date} is invalid. + */ + public static DateWithoutTime parseDateWithoutTime(String date) + throws ParseException { + requireNonNull(date); + String trimmedDate = date.trim(); + if (!DateWithoutTime.isValidDate(trimmedDate)) { + throw new ParseException(Date.MESSAGE_CONSTRAINTS); + } + return new DateWithoutTime(trimmedDate); + } + } diff --git a/src/main/java/seedu/address/logic/parser/UnmarkCommandParser.java b/src/main/java/seedu/address/logic/parser/UnmarkCommandParser.java new file mode 100644 index 00000000000..80b531010b2 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/UnmarkCommandParser.java @@ -0,0 +1,39 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.ArrayList; +import java.util.List; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.UnmarkCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +public class UnmarkCommandParser implements Parser { + public static final int GET_CLIENT_INDEX = 0; + public static final int GET_PROCEDURE_INDEX = 1; + + /** + * Parses the given {@code String} of arguments in the context of the UnmarkCommand + * and returns an UnmarkCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public UnmarkCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argumentMultimap = ArgumentTokenizer.tokenize(args); + List indexes = new ArrayList<>(); + try { + indexes.addAll(ParserUtil.parseIndexes(argumentMultimap.getPreamble())); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + UnmarkCommand.MESSAGE_USAGE), pe); + } + if (indexes.size() != 2) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + UnmarkCommand.MESSAGE_USAGE)); + } + + return new UnmarkCommand(indexes.get(GET_CLIENT_INDEX), indexes.get(GET_PROCEDURE_INDEX)); + } +} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index 1a943a0781a..d0e9a989fea 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -2,20 +2,23 @@ import static java.util.Objects.requireNonNull; +import java.util.ArrayList; import java.util.List; +import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; +import seedu.address.model.client.Client; +import seedu.address.model.client.UniqueClientList; +import seedu.address.model.procedure.Procedure; /** * Wraps all data at the address-book level - * Duplicates are not allowed (by .isSamePerson comparison) + * Duplicates are not allowed (by .isSameClient comparison) */ public class AddressBook implements ReadOnlyAddressBook { - private final UniquePersonList persons; - + private final UniqueClientList clients; + private List procedures; /* * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html @@ -24,27 +27,30 @@ public class AddressBook implements ReadOnlyAddressBook { * among constructors. */ { - persons = new UniquePersonList(); + clients = new UniqueClientList(); } - public AddressBook() {} + public AddressBook() { + procedures = new ArrayList<>(); + } /** - * Creates an AddressBook using the Persons in the {@code toBeCopied} + * Creates an AddressBook using the Clients in the {@code toBeCopied} */ public AddressBook(ReadOnlyAddressBook toBeCopied) { this(); resetData(toBeCopied); + procedures = toBeCopied.getProcedureList(); } //// list overwrite operations /** - * Replaces the contents of the person list with {@code persons}. - * {@code persons} must not contain duplicate persons. + * Replaces the contents of the client list with {@code clients}. + * {@code clients} must not contain duplicate clients. */ - public void setPersons(List persons) { - this.persons.setPersons(persons); + public void setClients(List clients) { + this.clients.setClients(clients); } /** @@ -53,68 +59,84 @@ public void setPersons(List persons) { public void resetData(ReadOnlyAddressBook newData) { requireNonNull(newData); - setPersons(newData.getPersonList()); + setClients(newData.getClientList()); + setProcedures(newData.getProcedureList()); } - //// person-level operations + //// client-level operations /** - * Returns true if a person with the same identity as {@code person} exists in the address book. + * Returns true if a client with the same address as {@code client} exists in the address book. */ - public boolean hasPerson(Person person) { - requireNonNull(person); - return persons.contains(person); + public boolean hasClient(Client client) { + requireNonNull(client); + return clients.contains(client); + } /** - * Adds a person to the address book. - * The person must not already exist in the address book. + * Adds a client to the address book. + * The client must not already exist in the address book. */ - public void addPerson(Person p) { - persons.add(p); + public void addClient(Client p) { + clients.add(p); } /** - * Replaces the given person {@code target} in the list with {@code editedPerson}. + * Replaces the given client {@code target} in the list with {@code editedClient}. * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. + * The client identity of {@code editedClient} must not be the same as another existing client in the address book. */ - public void setPerson(Person target, Person editedPerson) { - requireNonNull(editedPerson); + public void setClient(Client target, Client editedClient) { + requireNonNull(editedClient); - persons.setPerson(target, editedPerson); + clients.setClient(target, editedClient); } /** * Removes {@code key} from this {@code AddressBook}. * {@code key} must exist in the address book. */ - public void removePerson(Person key) { - persons.remove(key); + public void removeClient(Client key) { + clients.remove(key); + } + + /// procedure-related methods + + /** + * updates the list of {@code Procedure}. + */ + public void setProcedures(List procedureList) { + procedures = procedureList; } //// util methods @Override public String toString() { - return persons.asUnmodifiableObservableList().size() + " persons"; + return clients.asUnmodifiableObservableList().size() + " clients"; // TODO: refine later } @Override - public ObservableList getPersonList() { - return persons.asUnmodifiableObservableList(); + public ObservableList getClientList() { + return clients.asUnmodifiableObservableList(); + } + + @Override + public ObservableList getProcedureList() { + return FXCollections.observableList(procedures); } @Override public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof AddressBook // instanceof handles nulls - && persons.equals(((AddressBook) other).persons)); + && clients.equals(((AddressBook) other).clients)); } @Override public int hashCode() { - return persons.hashCode(); + return clients.hashCode(); } } diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index d54df471c1f..52af4d67721 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -1,19 +1,21 @@ package seedu.address.model; import java.nio.file.Path; +import java.util.List; import java.util.function.Predicate; import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; +import seedu.address.model.client.Client; +import seedu.address.model.procedure.Procedure; /** * The API of the Model component. */ public interface Model { /** {@code Predicate} that always evaluate to true */ - Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; - + Predicate PREDICATE_SHOW_ALL_CLIENTS = unused -> true; + Predicate PREDICATE_SHOW_CLIENT_PROCEDURES = unused -> true; /** * Replaces user prefs data with the data in {@code userPrefs}. */ @@ -53,35 +55,57 @@ public interface Model { ReadOnlyAddressBook getAddressBook(); /** - * Returns true if a person with the same identity as {@code person} exists in the address book. + * Returns true if a client with the same identity as {@code Client} exists in the address book. + */ + boolean hasClient(Client client); + + /** + * Deletes the given client. + * The client must exist in the address book. + */ + void deleteClient(Client target); + + /** + * Adds the given client. + * {@code Client} must not already exist in the address book. */ - boolean hasPerson(Person person); + void addClient(Client target); /** - * Deletes the given person. - * The person must exist in the address book. + * Delete the given Procedure. */ - void deletePerson(Person target); + void deleteProcedure(Procedure procedure); /** - * Adds the given person. - * {@code person} must not already exist in the address book. + * Add the given Procedure. */ - void addPerson(Person person); + void addProcedure(Procedure procedure); /** - * Replaces the given person {@code target} with {@code editedPerson}. + * Replaces the given client {@code target} with {@code editedClient}. * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. + * The client identity of {@code editedClient} must not be the same as another existing client in the address book. + */ + void setClient(Client target, Client editedClient); + + void setProcedures(List procedures); + + /** Returns an unmodifiable view of the filtered client list */ + ObservableList getFilteredClientList(); + + /** Returns an unmodifiable view of the filtered procedure list */ + ObservableList getFilteredProcedureList(); + + /** + * Updates the filter of the filtered client list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. */ - void setPerson(Person target, Person editedPerson); + void updateFilteredClientList(Predicate predicate); - /** Returns an unmodifiable view of the filtered person list */ - ObservableList getFilteredPersonList(); /** - * Updates the filter of the filtered person list to filter by the given {@code predicate}. + * Updates the filter of the filtered procedure list to filter by the given {@code predicate}. * @throws NullPointerException if {@code predicate} is null. */ - void updateFilteredPersonList(Predicate predicate); + void updateFilteredProcedureList(Client client, Predicate predicate); } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 86c1df298d7..1ede182ca7c 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -4,14 +4,18 @@ import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; import java.nio.file.Path; +import java.util.List; import java.util.function.Predicate; import java.util.logging.Logger; +import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; +import seedu.address.model.client.Client; +import seedu.address.model.procedure.Procedure; + /** * Represents the in-memory model of the address book data. @@ -21,7 +25,8 @@ public class ModelManager implements Model { private final AddressBook addressBook; private final UserPrefs userPrefs; - private final FilteredList filteredPersons; + private final FilteredList filteredClients; + private FilteredList filteredProcedures; /** * Initializes a ModelManager with the given addressBook and userPrefs. @@ -33,7 +38,8 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs this.addressBook = new AddressBook(addressBook); this.userPrefs = new UserPrefs(userPrefs); - filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); + filteredClients = new FilteredList<>(this.addressBook.getClientList()); + filteredProcedures = new FilteredList<>(this.addressBook.getProcedureList()); } public ModelManager() { @@ -80,6 +86,7 @@ public void setAddressBookFilePath(Path addressBookFilePath) { @Override public void setAddressBook(ReadOnlyAddressBook addressBook) { this.addressBook.resetData(addressBook); + this.filteredProcedures = new FilteredList<>(addressBook.getProcedureList()); } @Override @@ -88,44 +95,77 @@ public ReadOnlyAddressBook getAddressBook() { } @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return addressBook.hasPerson(person); + public boolean hasClient(Client client) { + requireNonNull(client); + return addressBook.hasClient(client); } @Override - public void deletePerson(Person target) { - addressBook.removePerson(target); + public void deleteClient(Client target) { + addressBook.removeClient(target); } @Override - public void addPerson(Person person) { - addressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + public void addClient(Client client) { + addressBook.addClient(client); + updateFilteredClientList(PREDICATE_SHOW_ALL_CLIENTS); } @Override - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); + public void deleteProcedure(Procedure procedure) { + } - addressBook.setPerson(target, editedPerson); + @Override + public void addProcedure(Procedure procedure) { } - //=========== Filtered Person List Accessors ============================================================= + @Override + public void setClient(Client target, Client editedClient) { + requireAllNonNull(target, editedClient); + + addressBook.setClient(target, editedClient); + } + + @Override + public void setProcedures(List procedures) { + addressBook.setProcedures(procedures); + filteredProcedures = new FilteredList<>(this.addressBook.getProcedureList()); + } + + //=========== Filtered Client List Accessors ============================================================= /** - * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of + * Returns an unmodifiable view of the list of {@code Client} backed by the internal list of * {@code versionedAddressBook} */ @Override - public ObservableList getFilteredPersonList() { - return filteredPersons; + public ObservableList getFilteredClientList() { + return filteredClients; } + /** + * Returns an observable list of {@code Procedure} of {@code Client}. + */ @Override - public void updateFilteredPersonList(Predicate predicate) { + public ObservableList getFilteredProcedureList() { + return filteredProcedures; + } + + /** + * Updates the filtered list of {@code Client} to match with a new {@code predicate}. + */ + public void updateFilteredClientList(Predicate predicate) { requireNonNull(predicate); - filteredPersons.setPredicate(predicate); + filteredClients.setPredicate(predicate); + } + + /** + * Updates the filtered list of {@code Procedure} to match with a new {@code predicate}. + */ + @Override + public void updateFilteredProcedureList(Client client, Predicate predicate) { + requireAllNonNull(client, predicate); + filteredProcedures = new FilteredList<>(FXCollections.observableArrayList(client.getProcedures()), predicate); } @Override @@ -144,7 +184,7 @@ public boolean equals(Object obj) { ModelManager other = (ModelManager) obj; return addressBook.equals(other.addressBook) && userPrefs.equals(other.userPrefs) - && filteredPersons.equals(other.filteredPersons); + && filteredClients.equals(other.filteredClients); } } diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java index 6ddc2cd9a29..58bc7281e3c 100644 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java @@ -1,17 +1,22 @@ package seedu.address.model; import javafx.collections.ObservableList; -import seedu.address.model.person.Person; +import seedu.address.model.client.Client; +import seedu.address.model.procedure.Procedure; /** - * Unmodifiable view of an address book + * Unmodifiable view of an address book. */ public interface ReadOnlyAddressBook { /** - * Returns an unmodifiable view of the persons list. - * This list will not contain any duplicate persons. + * Returns an unmodifiable view of the clients list. + * This list will not contain any duplicate clients. */ - ObservableList getPersonList(); + ObservableList getClientList(); + /** + * Returns a list of procedures. + */ + ObservableList getProcedureList(); } diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java index 25a5fd6eab9..a1572caedc0 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/address/model/UserPrefs.java @@ -14,7 +14,7 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path addressBookFilePath = Paths.get("data" , "networkers.json"); /** * Creates a {@code UserPrefs} with default values. diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/client/Address.java similarity index 94% rename from src/main/java/seedu/address/model/person/Address.java rename to src/main/java/seedu/address/model/client/Address.java index 60472ca22a0..4e66130c2f3 100644 --- a/src/main/java/seedu/address/model/person/Address.java +++ b/src/main/java/seedu/address/model/client/Address.java @@ -1,10 +1,10 @@ -package seedu.address.model.person; +package seedu.address.model.client; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's address in the address book. + * Represents a Client's address in the address book. * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} */ public class Address { diff --git a/src/main/java/seedu/address/model/client/Client.java b/src/main/java/seedu/address/model/client/Client.java new file mode 100644 index 00000000000..77095cf8862 --- /dev/null +++ b/src/main/java/seedu/address/model/client/Client.java @@ -0,0 +1,194 @@ +package seedu.address.model.client; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import seedu.address.commons.core.datewithouttime.DateWithoutTime; +import seedu.address.model.procedure.Date; +import seedu.address.model.procedure.Procedure; +import seedu.address.model.tag.Tag; + +/** + * Represents a Client in the address book. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Client { + + // Identity fields + private final Name name; + private final Phone phone; + private final Email email; + + // Data fields + private final Address address; + private final Plan plan; + private final Set tags = new HashSet<>(); + private final List procedures = new ArrayList(); + + /** + * Every field, less Procedures, must be present and not null. + */ + public Client(Name name, Phone phone, Email email, Address address, Plan plan, Set tags) { + requireAllNonNull(name, phone, email, address, tags); + this.name = name; + this.phone = phone; + this.email = email; + this.address = address; + this.plan = plan; + this.tags.addAll(tags); + } + + /** + * Every field must be present and not null. + */ + public Client(Name name, Phone phone, Email email, Address address, Plan plan, + Set tags, List procedures) { + requireAllNonNull(name, phone, email, address, tags); + this.name = name; + this.phone = phone; + this.email = email; + this.address = address; + this.plan = plan; + this.tags.addAll(tags); + this.procedures.addAll(procedures); + } + + public Name getName() { + return name; + } + + public Phone getPhone() { + return phone; + } + + public Email getEmail() { + return email; + } + + public Address getAddress() { + return address; + } + + public Plan getPlan() { + return plan; + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Set getTags() { + return Collections.unmodifiableSet(tags); + } + + /** + * Returns an immutable procedure set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public List getProcedures() { + return Collections.unmodifiableList(procedures); + } + + public List getProcsOnDate(DateWithoutTime date) { + List procsOnDate = new ArrayList (); + Date startTime = new Date(date.toString() + Date.START_OF_DAY); + Date endTime = new Date(date.toString() + Date.END_OF_DAY); + for (int i = 0; i < this.procedures.size(); i++) { + Procedure currentProc = this.procedures.get(i); + Date currentDate = currentProc.getDate(); + if (currentDate.compareTo(startTime) >= 0 + && currentDate.compareTo(endTime) <= 0) { + procsOnDate.add(currentProc); + } + } + return procsOnDate; + } + + public BigDecimal getCostOnDate(DateWithoutTime date) { + List procsOnDate = getProcsOnDate(date); + BigDecimal totalCost = new BigDecimal(0); + for (int i = 0; i < procsOnDate.size(); i++) { + Procedure currentProc = procsOnDate.get(i); + BigDecimal currentCost = currentProc.getCost().value(); + totalCost = totalCost.add(currentCost);; + } + return totalCost; + } + + /** + * Returns true if both clients have the same name. + * This defines a weaker notion of equality between two clients. + */ + public boolean isSameClient(Client otherClient) { + if (otherClient == this) { + return true; + } + + return otherClient != null + && otherClient.getAddress().equals(getAddress()); + } + + /** + * Returns true if both clients have the same identity and data fields. + * This defines a stronger notion of equality between two clients. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Client)) { + return false; + } + + Client otherClient = (Client) other; + return otherClient.getName().equals(getName()) + && otherClient.getPhone().equals(getPhone()) + && otherClient.getEmail().equals(getEmail()) + && otherClient.getAddress().equals(getAddress()) + && otherClient.getPlan().equals(getPlan()) + && otherClient.getTags().equals(getTags()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, phone, email, address, plan, tags); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName()) + .append("; Phone: ") + .append(getPhone()) + .append("; Email: ") + .append(getEmail()) + .append("; Address: ") + .append(getAddress()) + .append("; Plan: ") + .append(getPlan()); + + Set tags = getTags(); + if (!tags.isEmpty()) { + builder.append("; Tags: "); + tags.forEach(builder::append); + } + + List procedures = getProcedures(); + if (!procedures.isEmpty()) { + builder.append("; Procedures: "); + procedures.forEach(builder::append); + } + return builder.toString(); + } + +} diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/client/Email.java similarity index 97% rename from src/main/java/seedu/address/model/person/Email.java rename to src/main/java/seedu/address/model/client/Email.java index f866e7133de..832cd346c5a 100644 --- a/src/main/java/seedu/address/model/person/Email.java +++ b/src/main/java/seedu/address/model/client/Email.java @@ -1,10 +1,10 @@ -package seedu.address.model.person; +package seedu.address.model.client; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's email in the address book. + * Represents a Client's email in the address book. * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} */ public class Email { diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/client/Name.java similarity index 75% rename from src/main/java/seedu/address/model/person/Name.java rename to src/main/java/seedu/address/model/client/Name.java index 79244d71cf7..7aa5c249547 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/seedu/address/model/client/Name.java @@ -1,22 +1,21 @@ -package seedu.address.model.person; +package seedu.address.model.client; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's name in the address book. + * Represents a Client's name in the address book. * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} */ public class Name { - public static final String MESSAGE_CONSTRAINTS = - "Names should only contain alphanumeric characters and spaces, and it should not be blank"; + public static final String MESSAGE_CONSTRAINTS = "Names can take any values, and it should not be blank"; /* - * The first character of the address must not be a whitespace, + * The first character of the name must not be a whitespace, * otherwise " " (a blank string) becomes a valid input. */ - public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; + public static final String VALIDATION_REGEX = "[^\\s].*"; public final String fullName; diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/client/NameContainsKeywordsPredicate.java similarity index 80% rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/seedu/address/model/client/NameContainsKeywordsPredicate.java index c9b5868427c..74af1afcae8 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/seedu/address/model/client/NameContainsKeywordsPredicate.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.address.model.client; import java.util.List; import java.util.function.Predicate; @@ -6,9 +6,9 @@ import seedu.address.commons.util.StringUtil; /** - * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. + * Tests that a {@code Client}'s {@code Name} matches any of the keywords given. */ -public class NameContainsKeywordsPredicate implements Predicate { +public class NameContainsKeywordsPredicate implements Predicate { private final List keywords; public NameContainsKeywordsPredicate(List keywords) { @@ -16,9 +16,9 @@ public NameContainsKeywordsPredicate(List keywords) { } @Override - public boolean test(Person person) { + public boolean test(Client client) { return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(client.getName().fullName, keyword)); } @Override diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/client/Phone.java similarity index 93% rename from src/main/java/seedu/address/model/person/Phone.java rename to src/main/java/seedu/address/model/client/Phone.java index 872c76b382f..36243710d87 100644 --- a/src/main/java/seedu/address/model/person/Phone.java +++ b/src/main/java/seedu/address/model/client/Phone.java @@ -1,10 +1,10 @@ -package seedu.address.model.person; +package seedu.address.model.client; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's phone number in the address book. + * Represents a Client's phone number in the address book. * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} */ public class Phone { diff --git a/src/main/java/seedu/address/model/client/Plan.java b/src/main/java/seedu/address/model/client/Plan.java new file mode 100644 index 00000000000..86f398c8264 --- /dev/null +++ b/src/main/java/seedu/address/model/client/Plan.java @@ -0,0 +1,55 @@ +package seedu.address.model.client; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Client's subscription plan of its network. + * Guarantees: immutable; is valid as declared in {@link #isValidPlan(String)} + */ +public class Plan { + public static final String MESSAGE_CONSTRAINTS = "Plans can take any values, and it should not be blank"; + + /* + * The first character of the plan must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[^\\s].*"; + + public final String value; + + /** + * Constructs an {@code Plan}. + * + * @param plan A valid subscription plan. + */ + public Plan(String plan) { + requireNonNull(plan); + checkArgument(isValidPlan(plan), MESSAGE_CONSTRAINTS); + value = plan; + } + + /** + * Returns true if a given string is a valid email. + */ + public static boolean isValidPlan(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Plan // instanceof handles nulls + && value.equals(((Plan) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/client/UniqueClientList.java b/src/main/java/seedu/address/model/client/UniqueClientList.java new file mode 100644 index 00000000000..eafb5bd8cc1 --- /dev/null +++ b/src/main/java/seedu/address/model/client/UniqueClientList.java @@ -0,0 +1,137 @@ +package seedu.address.model.client; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.client.exceptions.ClientNotFoundException; +import seedu.address.model.client.exceptions.DuplicateClientException; + +/** + * A list of clients that enforces uniqueness between its elements and does not allow nulls. + * A client is considered unique by comparing using {@code Client#isSameClient(Client)}. As such, adding and updating of + * clients uses Client#isSameClient(Client) for equality so as to ensure that the client being added or updated is + * unique in terms of identity in the UniqueClientList. However, the removal of a client uses Client#equals(Object) so + * as to ensure that the client with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Client#isSameClient(Client) + */ +public class UniqueClientList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent client as the given argument. + */ + public boolean contains(Client toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameClient); + } + + /** + * Adds a client to the list. + * The client must not already exist in the list. + */ + public void add(Client toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateClientException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the client {@code target} in the list with {@code editedClient}. + * {@code target} must exist in the list. + * The client identity of {@code editedClient} must not be the same as another existing client in the list. + */ + public void setClient(Client target, Client editedClient) { + requireAllNonNull(target, editedClient); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new ClientNotFoundException(); + } + + if (!target.isSameClient(editedClient) && contains(editedClient)) { + throw new DuplicateClientException(); + } + + internalList.set(index, editedClient); + } + + /** + * Removes the equivalent client from the list. + * The client must exist in the list. + */ + public void remove(Client toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new ClientNotFoundException(); + } + } + + public void setClients(UniqueClientList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code clients}. + * {@code clients} must not contain duplicate clients. + */ + public void setClients(List clients) { + requireAllNonNull(clients); + if (!clientsAreUnique(clients)) { + throw new DuplicateClientException(); + } + + internalList.setAll(clients); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueClientList // instanceof handles nulls + && internalList.equals(((UniqueClientList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code clients} contains only unique clients. + */ + private boolean clientsAreUnique(List clients) { + for (int i = 0; i < clients.size() - 1; i++) { + for (int j = i + 1; j < clients.size(); j++) { + if (clients.get(i).isSameClient(clients.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/address/model/client/exceptions/ClientNotFoundException.java b/src/main/java/seedu/address/model/client/exceptions/ClientNotFoundException.java new file mode 100644 index 00000000000..84f9ae2c085 --- /dev/null +++ b/src/main/java/seedu/address/model/client/exceptions/ClientNotFoundException.java @@ -0,0 +1,6 @@ +package seedu.address.model.client.exceptions; + +/** + * Signals that the operation is unable to find the specified client. + */ +public class ClientNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/client/exceptions/DuplicateClientException.java b/src/main/java/seedu/address/model/client/exceptions/DuplicateClientException.java new file mode 100644 index 00000000000..ef4e36ea4aa --- /dev/null +++ b/src/main/java/seedu/address/model/client/exceptions/DuplicateClientException.java @@ -0,0 +1,11 @@ +package seedu.address.model.client.exceptions; + +/** + * Signals that the operation will result in duplicate Clients (Clients are considered duplicates if they have the same + * identity). + */ +public class DuplicateClientException extends RuntimeException { + public DuplicateClientException() { + super("Operation would result in duplicate clients"); + } +} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java deleted file mode 100644 index 8ff1d83fe89..00000000000 --- a/src/main/java/seedu/address/model/person/Person.java +++ /dev/null @@ -1,123 +0,0 @@ -package seedu.address.model.person; - -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import seedu.address.model.tag.Tag; - -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. - */ -public class Person { - - // Identity fields - private final Name name; - private final Phone phone; - private final Email email; - - // Data fields - private final Address address; - private final Set tags = new HashSet<>(); - - /** - * Every field must be present and not null. - */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - requireAllNonNull(name, phone, email, address, tags); - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - this.tags.addAll(tags); - } - - public Name getName() { - return name; - } - - public Phone getPhone() { - return phone; - } - - public Email getEmail() { - return email; - } - - public Address getAddress() { - return address; - } - - /** - * Returns an immutable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - */ - public Set getTags() { - return Collections.unmodifiableSet(tags); - } - - /** - * Returns true if both persons have the same name. - * This defines a weaker notion of equality between two persons. - */ - public boolean isSamePerson(Person otherPerson) { - if (otherPerson == this) { - return true; - } - - return otherPerson != null - && otherPerson.getName().equals(getName()); - } - - /** - * Returns true if both persons have the same identity and data fields. - * This defines a stronger notion of equality between two persons. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof Person)) { - return false; - } - - Person otherPerson = (Person) other; - return otherPerson.getName().equals(getName()) - && otherPerson.getPhone().equals(getPhone()) - && otherPerson.getEmail().equals(getEmail()) - && otherPerson.getAddress().equals(getAddress()) - && otherPerson.getTags().equals(getTags()); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append(getName()) - .append("; Phone: ") - .append(getPhone()) - .append("; Email: ") - .append(getEmail()) - .append("; Address: ") - .append(getAddress()); - - Set tags = getTags(); - if (!tags.isEmpty()) { - builder.append("; Tags: "); - tags.forEach(builder::append); - } - return builder.toString(); - } - -} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java deleted file mode 100644 index 0fee4fe57e6..00000000000 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ /dev/null @@ -1,137 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Iterator; -import java.util.List; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; - -/** - * A list of persons that enforces uniqueness between its elements and does not allow nulls. - * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of - * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is - * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so - * as to ensure that the person with exactly the same fields will be removed. - * - * Supports a minimal set of list operations. - * - * @see Person#isSamePerson(Person) - */ -public class UniquePersonList implements Iterable { - - private final ObservableList internalList = FXCollections.observableArrayList(); - private final ObservableList internalUnmodifiableList = - FXCollections.unmodifiableObservableList(internalList); - - /** - * Returns true if the list contains an equivalent person as the given argument. - */ - public boolean contains(Person toCheck) { - requireNonNull(toCheck); - return internalList.stream().anyMatch(toCheck::isSamePerson); - } - - /** - * Adds a person to the list. - * The person must not already exist in the list. - */ - public void add(Person toAdd) { - requireNonNull(toAdd); - if (contains(toAdd)) { - throw new DuplicatePersonException(); - } - internalList.add(toAdd); - } - - /** - * Replaces the person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the list. - * The person identity of {@code editedPerson} must not be the same as another existing person in the list. - */ - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - int index = internalList.indexOf(target); - if (index == -1) { - throw new PersonNotFoundException(); - } - - if (!target.isSamePerson(editedPerson) && contains(editedPerson)) { - throw new DuplicatePersonException(); - } - - internalList.set(index, editedPerson); - } - - /** - * Removes the equivalent person from the list. - * The person must exist in the list. - */ - public void remove(Person toRemove) { - requireNonNull(toRemove); - if (!internalList.remove(toRemove)) { - throw new PersonNotFoundException(); - } - } - - public void setPersons(UniquePersonList replacement) { - requireNonNull(replacement); - internalList.setAll(replacement.internalList); - } - - /** - * Replaces the contents of this list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - requireAllNonNull(persons); - if (!personsAreUnique(persons)) { - throw new DuplicatePersonException(); - } - - internalList.setAll(persons); - } - - /** - * Returns the backing list as an unmodifiable {@code ObservableList}. - */ - public ObservableList asUnmodifiableObservableList() { - return internalUnmodifiableList; - } - - @Override - public Iterator iterator() { - return internalList.iterator(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof UniquePersonList // instanceof handles nulls - && internalList.equals(((UniquePersonList) other).internalList)); - } - - @Override - public int hashCode() { - return internalList.hashCode(); - } - - /** - * Returns true if {@code persons} contains only unique persons. - */ - private boolean personsAreUnique(List persons) { - for (int i = 0; i < persons.size() - 1; i++) { - for (int j = i + 1; j < persons.size(); j++) { - if (persons.get(i).isSamePerson(persons.get(j))) { - return false; - } - } - } - return true; - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java deleted file mode 100644 index d7290f59442..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ /dev/null @@ -1,11 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same - * identity). - */ -public class DuplicatePersonException extends RuntimeException { - public DuplicatePersonException() { - super("Operation would result in duplicate persons"); - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java deleted file mode 100644 index fa764426ca7..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ /dev/null @@ -1,6 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation is unable to find the specified person. - */ -public class PersonNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/procedure/Completion.java b/src/main/java/seedu/address/model/procedure/Completion.java new file mode 100644 index 00000000000..a8f6135214b --- /dev/null +++ b/src/main/java/seedu/address/model/procedure/Completion.java @@ -0,0 +1,52 @@ +package seedu.address.model.procedure; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +public class Completion { + public static final String MESSAGE_CONSTRAINTS = "1. hasCompleted should be a Boolean value, " + + "Boolean.TRUE or Boolean.FALSE"; + + public static final String VALIDATION_REGEX = "^([t][r][u][e]|[f][a][l][s][e])$"; + public final Boolean hasCompleted; + + /** + * Constructs a {@code Completion}. + * + * @param hasCompleted A valid Boolean value indicating hasCompleted of Procedure. + */ + public Completion(String hasCompleted) { + requireNonNull(hasCompleted); + checkArgument(isValidHasCompleted(hasCompleted), MESSAGE_CONSTRAINTS); + this.hasCompleted = Boolean.parseBoolean(hasCompleted); + } + + /** + * Returns true if a given string is a valid boolean. + * + */ + public static boolean isValidHasCompleted(String test) { + return test.matches(VALIDATION_REGEX); + } + + public Boolean getHasCompleted() { + return this.hasCompleted; + } + + @Override + public String toString() { + return this.hasCompleted.toString(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Completion // instanceof handles nulls + && this.getHasCompleted().equals(((Completion) other).getHasCompleted())); // state check + } + + @Override + public int hashCode() { + return this.hasCompleted.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/procedure/Cost.java b/src/main/java/seedu/address/model/procedure/Cost.java new file mode 100644 index 00000000000..af5c0e27678 --- /dev/null +++ b/src/main/java/seedu/address/model/procedure/Cost.java @@ -0,0 +1,72 @@ +package seedu.address.model.procedure; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.math.BigDecimal; + + +public class Cost implements Comparable { + + public static final String MESSAGE_CONSTRAINTS = + "1. Cost should be rounded to the nearest cent. \n" + + "2. Cost should be more than $0 and less than $100000000 (One hundred million dollars) \n" + + "3. Cost should follow the format 'dollars.cents'.\n" + + "Example: 31.10"; + + public static final String VALIDATION_REGEX = "^\\d+(?:\\.\\d{1,2})?$"; + public static final BigDecimal MAX_COST = new BigDecimal(100000000); + public static final BigDecimal MIN_COST = new BigDecimal(0); + + public final BigDecimal cost; + + /** + * Constructs a {@code Cost}. + * + * @param cost A valid cost. + */ + public Cost(String cost) { + requireNonNull(cost); + checkArgument(isValidCost(cost), MESSAGE_CONSTRAINTS); + this.cost = new BigDecimal(cost).setScale(2, BigDecimal.ROUND_UP); + } + + /** + * Returns true if a given string is a valid cost. + */ + public static boolean isValidCost(String test) { + if (test.matches(VALIDATION_REGEX)) { + BigDecimal cost = new BigDecimal(test); + if (cost.compareTo(MAX_COST) < 0 && cost.compareTo(MIN_COST) > 0) { + return true; + } + } + return false; + } + + public BigDecimal value() { + return this.cost; + } + + @Override + public String toString() { + return this.cost.toString(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof seedu.address.model.procedure.Cost // instanceof handles nulls + && this.cost.equals(((seedu.address.model.procedure.Cost) other).cost)); // state check + } + + @Override + public int hashCode() { + return this.cost.hashCode(); + } + + @Override + public int compareTo(Cost otherCost) { + return cost.compareTo(otherCost.cost); + } +} diff --git a/src/main/java/seedu/address/model/procedure/Date.java b/src/main/java/seedu/address/model/procedure/Date.java new file mode 100644 index 00000000000..ae377234673 --- /dev/null +++ b/src/main/java/seedu/address/model/procedure/Date.java @@ -0,0 +1,85 @@ +package seedu.address.model.procedure; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.format.ResolverStyle; + +/** + * Represents a Procedure's date in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidDate(String)} + */ +public class Date implements Comparable { + + public static final String MESSAGE_CONSTRAINTS = + "Please ensure that you have typed an existing date in the correct format. \n" + + "Dates should be in the format DD/MM/YYYY hh:mm, and it should not be blank \n" + + "Years can take any value in the range 0000 to 9999"; + + /* + * The first character of the address must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "\\d{2}[/]\\d{2}[/]\\d{4} \\d{2}:\\d{2}"; + + public static final DateTimeFormatter FORMAT_WITH_DATE = DateTimeFormatter + .ofPattern("dd/MM/uuuu HH:mm"); + + public static final String START_OF_DAY = " 00:00"; + + public static final String END_OF_DAY = " 23:59"; + + public final LocalDateTime validDate; + + /** + * Constructs a {@code Date}. + * + * @param date A valid date. + */ + public Date(String date) { + requireNonNull(date); + checkArgument(isValidDate(date), MESSAGE_CONSTRAINTS); + validDate = LocalDateTime.parse(date, FORMAT_WITH_DATE); + } + + /** + * Returns true if a given string is a valid date. + */ + public static boolean isValidDate(String test) { + if (test.matches(VALIDATION_REGEX)) { + try { + LocalDateTime.parse(test, FORMAT_WITH_DATE + .withResolverStyle(ResolverStyle.STRICT)); + return true; + } catch (DateTimeParseException err) { + return false; + } + } + return false; + } + + @Override + public String toString() { + return validDate.format(FORMAT_WITH_DATE); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Date // instanceof handles nulls + && validDate.equals(((Date) other).validDate)); // state check + } + + @Override + public int hashCode() { + return validDate.hashCode(); + } + + @Override + public int compareTo(Date otherDate) { + return validDate.compareTo(otherDate.validDate); + } +} diff --git a/src/main/java/seedu/address/model/procedure/Information.java b/src/main/java/seedu/address/model/procedure/Information.java new file mode 100644 index 00000000000..75b79fd82ab --- /dev/null +++ b/src/main/java/seedu/address/model/procedure/Information.java @@ -0,0 +1,62 @@ +package seedu.address.model.procedure; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Procedure's information in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidInformation(String)} + */ +public class Information implements Comparable { + + public static final String MESSAGE_CONSTRAINTS = + "Information can take any values but it should not be blank"; + + /* + * The first character of the information must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[^\\s].*"; + + public final String info; + + /** + * Constructs an {@code Information}. + * + * @param info A valid Information. + */ + public Information(String info) { + requireNonNull(info); + checkArgument(isValidInformation(info), MESSAGE_CONSTRAINTS); + this.info = info; + } + + /** + * Returns true if a given string is a valid information. + */ + public static boolean isValidInformation(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return info; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Information // instanceof handles nulls + && info.equals(((Information) other).info)); // state check + } + + @Override + public int hashCode() { + return info.hashCode(); + } + + @Override + public int compareTo(Information otherInfo) { + return info.compareTo(otherInfo.info); + } +} diff --git a/src/main/java/seedu/address/model/procedure/Procedure.java b/src/main/java/seedu/address/model/procedure/Procedure.java new file mode 100644 index 00000000000..7baa878038a --- /dev/null +++ b/src/main/java/seedu/address/model/procedure/Procedure.java @@ -0,0 +1,118 @@ +package seedu.address.model.procedure; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Objects; + +/** + * Represents a specific procedure in a client. + */ +public class Procedure implements Comparable { + private final Information information; + private final Date date; + private final Cost cost; + private Completion hasCompleted; + + /** + * Every field must be present and not null. + */ + public Procedure(Information information, Date date, Cost cost, Completion hasCompleted) { + requireAllNonNull(information, date, cost); + this.information = information; + this.date = date; + this.cost = cost; + this.hasCompleted = hasCompleted; + } + + public Information getInfo() { + return this.information; + } + + public Date getDate() { + return this.date; + } + + public Cost getCost() { + return this.cost; + } + + public Completion getHasCompleted() { + return this.hasCompleted; + } + + public void setHasCompleted(Completion hasCompleted) { + this.hasCompleted = hasCompleted; + } + + /** + * Returns true if both Procedures have the same fields, less the completion field. + */ + public boolean isProcedureDuplicate(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Procedure)) { + return false; + } + + Procedure otherPerson = (Procedure) other; + return otherPerson.getInfo().equals(getInfo()) + && otherPerson.getDate().equals(getDate()) + && otherPerson.getCost().equals(getCost()); + } + + /** + * Returns true if both Procedures have the same fields. + * This defines a stronger notion of equality between two Procedures. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Procedure)) { + return false; + } + + Procedure otherProc = (Procedure) other; + return otherProc.getInfo().equals(getInfo()) + && otherProc.getDate().equals(getDate()) + && otherProc.getCost().equals(getCost()) + && otherProc.getHasCompleted().equals(getHasCompleted()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(information, date, cost, hasCompleted); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("Information: ") + .append(getInfo()) + .append("; Date: ") + .append(getDate()) + .append("; Cost: $") + .append(getCost()) + .append("; Completed: ") + .append(getHasCompleted()); + + return builder.toString(); + } + + @Override + public int compareTo(Procedure otherProcedure) { + if (this.getDate().compareTo(otherProcedure.getDate()) != 0) { + return this.getDate().compareTo(otherProcedure.getDate()); + } else if (this.getCost().compareTo(otherProcedure.getCost()) != 0) { + return this.getCost().compareTo(otherProcedure.getCost()); + } else { + return this.getInfo().compareTo(otherProcedure.getInfo()); + } + } +} + diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 1806da4facf..50aed78f460 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -1,49 +1,54 @@ package seedu.address.model.util; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Set; import java.util.stream.Collectors; import seedu.address.model.AddressBook; import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; +import seedu.address.model.client.Address; +import seedu.address.model.client.Client; +import seedu.address.model.client.Email; +import seedu.address.model.client.Name; +import seedu.address.model.client.Phone; +import seedu.address.model.client.Plan; +import seedu.address.model.procedure.Completion; +import seedu.address.model.procedure.Cost; +import seedu.address.model.procedure.Date; +import seedu.address.model.procedure.Information; +import seedu.address.model.procedure.Procedure; import seedu.address.model.tag.Tag; /** * Contains utility methods for populating {@code AddressBook} with sample data. */ public class SampleDataUtil { - public static Person[] getSamplePersons() { - return new Person[] { - new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), - new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), - new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), - new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), - new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), - new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) + public static Client[] getSampleClients() { + return new Client[] { + new Client(new Name("MINISO"), new Phone("65705231"), new Email("miniso@example.com"), + new Address("3155 Commonwealth Ave W, #03-56-58"), new Plan("EXPRESS 200MBps"), + getTagSet("maintenance", "periodic"), getSampleProceduresList1()), + + new Client(new Name("Master Fix Services"), new Phone("96724552"), new Email("masterfix@example.com"), + new Address("3155 Commonwealth Ave W, #B1-10"), new Plan("NORMAL 100MBps"), + getTagSet("maintenance", "SME"), getSampleProceduresList2()), + + new Client(new Name("Mr Bean"), new Phone("66594724"), new Email("mrbean@example.com"), + new Address("3155 Commonwealth Ave W, #B1-K13"), new Plan("VALUE 50MBps"), + getTagSet("household"), getSampleProceduresList3()), + + new Client(new Name("Optical 88"), new Phone("66595327"), new Email("optical88@example.com"), + new Address("3155 Commonwealth Ave W, #05-27"), new Plan("EXPRESS 200MBps"), + getTagSet("new"), getSampleProceduresList4()), }; } public static ReadOnlyAddressBook getSampleAddressBook() { AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); + for (Client sampleClient : getSampleClients()) { + sampleAb.addClient(sampleClient); } return sampleAb; } @@ -57,4 +62,103 @@ public static Set getTagSet(String... strings) { .collect(Collectors.toSet()); } + /** + * Returns a list of sample procedures. + */ + public static List getSampleProceduresList1() { + Procedure sampleProcedure1 = new Procedure(new Information("Fix Modem"), new Date("01/01/2021 15:55"), + new Cost("50"), new Completion("true")); + + Procedure sampleProcedure2 = new Procedure(new Information("Troubleshoot their router"), + new Date("03/03/2022 15:35"), new Cost("60"), new Completion("true")); + + Procedure sampleProcedure3 = new Procedure(new Information("Run a network diagnostic test"), + new Date("05/04/2022 11:55"), new Cost("10"), new Completion("true")); + + Procedure sampleProcedure4 = new Procedure(new Information("Extend international warranty of modem"), + new Date("07/05/2022 12:25"), new Cost("125"), new Completion("false")); + + Procedure sampleProcedure5 = new Procedure( + new Information("Inspect the network performance of their cash register"), + new Date("07/08/2022 13:15"), new Cost("100"), new Completion("false")); + + Procedure sampleProcedure6 = new Procedure( + new Information("Back up their employee data to the cloud database"), + new Date("10/09/2022 09:55"), new Cost("48"), new Completion("false")); + + ArrayList sampleProcedures = new ArrayList<>(); + sampleProcedures.add(sampleProcedure1); + sampleProcedures.add(sampleProcedure2); + sampleProcedures.add(sampleProcedure3); + sampleProcedures.add(sampleProcedure4); + sampleProcedures.add(sampleProcedure5); + sampleProcedures.add(sampleProcedure6); + return sampleProcedures; + } + + /** + * Returns a list of sample procedures. + */ + public static List getSampleProceduresList2() { + Procedure sampleProcedure1 = new Procedure(new Information("Run a network diagnostic test"), + new Date("30/05/2022 11:55"), new Cost("20"), new Completion("false")); + + Procedure sampleProcedure2 = new Procedure(new Information("Troubleshoot their router"), + new Date("31/05/2022 15:35"), new Cost("65"), new Completion("false")); + + Procedure sampleProcedure3 = new Procedure(new Information("Extend international warranty of modem"), + new Date("01/06/2022 09:25"), new Cost("195"), new Completion("false")); + + Procedure sampleProcedure4 = new Procedure( + new Information("Back up their employee data to the cloud database"), + new Date("06/06/2022 09:55"), new Cost("230"), new Completion("false")); + + ArrayList sampleProcedures = new ArrayList<>(); + sampleProcedures.add(sampleProcedure1); + sampleProcedures.add(sampleProcedure2); + sampleProcedures.add(sampleProcedure3); + sampleProcedures.add(sampleProcedure4); + return sampleProcedures; + } + + /** + * Returns a list of sample procedures. + */ + public static List getSampleProceduresList3() { + Procedure sampleProcedure1 = new Procedure(new Information("Extend international warranty of modem"), + new Date("01/06/2022 12:25"), new Cost("195"), new Completion("false")); + + Procedure sampleProcedure2 = new Procedure( + new Information("Back up their employee data to the cloud database"), + new Date("06/06/2022 09:55"), new Cost("230"), new Completion("false")); + + ArrayList sampleProcedures = new ArrayList<>(); + sampleProcedures.add(sampleProcedure1); + sampleProcedures.add(sampleProcedure2); + + return sampleProcedures; + } + + /** + * Returns a list of sample procedures. + */ + public static List getSampleProceduresList4() { + Procedure sampleProcedure1 = new Procedure(new Information("Extend international warranty of modem"), + new Date("06/06/2022 12:25"), new Cost("195"), new Completion("false")); + + Procedure sampleProcedure2 = new Procedure( + new Information("Back up their employee data to the cloud database"), + new Date("20/06/2022 09:55"), new Cost("230"), new Completion("false")); + + Procedure sampleProcedure3 = new Procedure( + new Information("Inspect the network performance of their cash register"), + new Date("23/08/2022 13:15"), new Cost("50"), new Completion("false")); + + ArrayList sampleProcedures = new ArrayList<>(); + sampleProcedures.add(sampleProcedure1); + sampleProcedures.add(sampleProcedure2); + sampleProcedures.add(sampleProcedure3); + + return sampleProcedures; + } } diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedClient.java similarity index 53% rename from src/main/java/seedu/address/storage/JsonAdaptedPerson.java rename to src/main/java/seedu/address/storage/JsonAdaptedClient.java index a6321cec2ea..8617bfee9a9 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ b/src/main/java/seedu/address/storage/JsonAdaptedClient.java @@ -10,64 +10,82 @@ import com.fasterxml.jackson.annotation.JsonProperty; import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; +import seedu.address.model.client.Address; +import seedu.address.model.client.Client; +import seedu.address.model.client.Email; +import seedu.address.model.client.Name; +import seedu.address.model.client.Phone; +import seedu.address.model.client.Plan; +import seedu.address.model.procedure.Procedure; import seedu.address.model.tag.Tag; /** - * Jackson-friendly version of {@link Person}. + * Jackson-friendly version of {@link Client}. */ -class JsonAdaptedPerson { +class JsonAdaptedClient { - public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Client's %s field is missing!"; private final String name; private final String phone; private final String email; private final String address; + private final String plan; private final List tagged = new ArrayList<>(); + private final List procedures = new ArrayList<>(); /** - * Constructs a {@code JsonAdaptedPerson} with the given person details. + * Constructs a {@code JsonAdaptedClient} with the given client details. */ @JsonCreator - public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone, + public JsonAdaptedClient(@JsonProperty("name") String name, @JsonProperty("phone") String phone, @JsonProperty("email") String email, @JsonProperty("address") String address, - @JsonProperty("tagged") List tagged) { + @JsonProperty("plan") String plan, @JsonProperty("tagged") List tagged, + @JsonProperty("procedures") List procedures) { this.name = name; this.phone = phone; this.email = email; this.address = address; + this.plan = plan; if (tagged != null) { this.tagged.addAll(tagged); } + if (procedures != null) { + this.procedures.addAll(procedures); + } } /** - * Converts a given {@code Person} into this class for Jackson use. + * Converts a given {@code Client} into this class for Jackson use. */ - public JsonAdaptedPerson(Person source) { + public JsonAdaptedClient(Client source) { name = source.getName().fullName; phone = source.getPhone().value; email = source.getEmail().value; address = source.getAddress().value; + plan = source.getPlan().value; tagged.addAll(source.getTags().stream() .map(JsonAdaptedTag::new) .collect(Collectors.toList())); + procedures.addAll(source.getProcedures().stream() + .map(JsonAdaptedProcedure::new) + .collect(Collectors.toList())); } /** - * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. + * Converts this Jackson-friendly adapted client object into the model's {@code Client} object. * - * @throws IllegalValueException if there were any data constraints violated in the adapted person. + * @throws IllegalValueException if there were any data constraints violated in the adapted client. */ - public Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); + public Client toModelType() throws IllegalValueException { + final List clientTags = new ArrayList<>(); for (JsonAdaptedTag tag : tagged) { - personTags.add(tag.toModelType()); + clientTags.add(tag.toModelType()); + } + + final List clientProcedures = new ArrayList<>(); + for (JsonAdaptedProcedure procedure : procedures) { + clientProcedures.add(procedure.toModelType()); } if (name == null) { @@ -102,8 +120,19 @@ public Person toModelType() throws IllegalValueException { } final Address modelAddress = new Address(address); - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); + if (plan == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Plan.class.getSimpleName())); + } + if (!Plan.isValidPlan(plan)) { + throw new IllegalValueException(Plan.MESSAGE_CONSTRAINTS); + } + final Plan modelPlan = new Plan(plan); + + final Set modelTags = new HashSet<>(clientTags); + + final ArrayList modelProcedures = new ArrayList<>(clientProcedures); + + return new Client(modelName, modelPhone, modelEmail, modelAddress, modelPlan, modelTags, modelProcedures); } } diff --git a/src/main/java/seedu/address/storage/JsonAdaptedProcedure.java b/src/main/java/seedu/address/storage/JsonAdaptedProcedure.java new file mode 100644 index 00000000000..57a7d47b583 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedProcedure.java @@ -0,0 +1,90 @@ +package seedu.address.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.procedure.Completion; +import seedu.address.model.procedure.Cost; +import seedu.address.model.procedure.Date; +import seedu.address.model.procedure.Information; +import seedu.address.model.procedure.Procedure; + + +public class JsonAdaptedProcedure { + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Procedure's %s field is missing!"; + + private final String information; + private final String date; + private final String cost; + private final String hasCompleted; + + /** + * Constructs a {@code JsonAdaptedProcedure} with the given procedure details. + */ + @JsonCreator + public JsonAdaptedProcedure(@JsonProperty("information") String information, + @JsonProperty("date") String date, @JsonProperty("cost") String cost, + @JsonProperty("hasCompleted") String hasCompleted) { + this.information = information; + this.date = date; + this.cost = cost; + this.hasCompleted = hasCompleted; + } + + /** + * Converts a given {@code Procedure} into this class for Jackson use. + */ + public JsonAdaptedProcedure(Procedure source) { + information = source.getInfo().info; + date = source.getDate().toString(); + cost = source.getCost().toString(); + hasCompleted = source.getHasCompleted().toString(); + } + + /** + * Converts this Jackson-friendly adapted procClient object into the model's {@code Procedure} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted client. + */ + public Procedure toModelType() throws IllegalValueException { + if (information == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Information.class.getSimpleName())); + } + if (!Information.isValidInformation(information)) { + throw new IllegalValueException(Information.MESSAGE_CONSTRAINTS); + } + final Information modelInfo = new Information(information); + + if (date == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Date.class.getSimpleName())); + } + if (!Date.isValidDate(date)) { + throw new IllegalValueException(Date.MESSAGE_CONSTRAINTS); + } + final Date modelDate = new Date(date); + + if (cost == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Cost.class.getSimpleName())); + } + if (!Cost.isValidCost(cost)) { + throw new IllegalValueException(Cost.MESSAGE_CONSTRAINTS); + } + final Cost modelCost = new Cost(cost); + + if (hasCompleted == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Completion.class.getSimpleName())); + } + if (!Completion.isValidHasCompleted(hasCompleted)) { + throw new IllegalValueException(Completion.MESSAGE_CONSTRAINTS); + } + final Completion modelCompletion = new Completion(hasCompleted); + + + return new Procedure(modelInfo, modelDate, modelCost, modelCompletion); + } +} diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java index 5efd834091d..d9bc46d2dbf 100644 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java @@ -11,7 +11,7 @@ import seedu.address.commons.exceptions.IllegalValueException; import seedu.address.model.AddressBook; import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.client.Client; /** * An Immutable AddressBook that is serializable to JSON format. @@ -19,16 +19,16 @@ @JsonRootName(value = "addressbook") class JsonSerializableAddressBook { - public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; + public static final String MESSAGE_DUPLICATE_CLIENT = "Clients list contains duplicate client(s)."; - private final List persons = new ArrayList<>(); + private final List clients = new ArrayList<>(); /** - * Constructs a {@code JsonSerializableAddressBook} with the given persons. + * Constructs a {@code JsonSerializableAddressBook} with the given clients. */ @JsonCreator - public JsonSerializableAddressBook(@JsonProperty("persons") List persons) { - this.persons.addAll(persons); + public JsonSerializableAddressBook(@JsonProperty("clients") List clients) { + this.clients.addAll(clients); } /** @@ -37,7 +37,7 @@ public JsonSerializableAddressBook(@JsonProperty("persons") List { +public class ClientCard extends UiPart { - private static final String FXML = "PersonListCard.fxml"; + private static final String FXML = "ClientListCard.fxml"; /** * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. @@ -24,7 +24,7 @@ public class PersonCard extends UiPart { * @see The issue on AddressBook level 4 */ - public final Person person; + public final Client client; @FXML private HBox cardPane; @@ -37,24 +37,32 @@ public class PersonCard extends UiPart { @FXML private Label address; @FXML + private Label plan; + @FXML private Label email; @FXML private FlowPane tags; /** - * Creates a {@code PersonCode} with the given {@code Person} and index to display. + * Creates a {@code ClientCode} with the given {@code Client} and index to display. */ - public PersonCard(Person person, int displayedIndex) { + public ClientCard(Client client, int displayedIndex) { super(FXML); - this.person = person; + this.client = client; id.setText(displayedIndex + ". "); - name.setText(person.getName().fullName); - phone.setText(person.getPhone().value); - address.setText(person.getAddress().value); - email.setText(person.getEmail().value); - person.getTags().stream() + name.setText(client.getName().fullName); + phone.setText(client.getPhone().value); + address.setText(client.getAddress().value); + plan.setText(client.getPlan().value); + email.setText(client.getEmail().value); + client.getTags().stream() .sorted(Comparator.comparing(tag -> tag.tagName)) - .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + .forEach(tag -> { + Label tagLabel = new Label(tag.tagName); + tagLabel.setWrapText(true); + tagLabel.setMaxWidth(400); + tags.getChildren().add(tagLabel); + }); } @Override @@ -65,13 +73,13 @@ public boolean equals(Object other) { } // instanceof handles nulls - if (!(other instanceof PersonCard)) { + if (!(other instanceof ClientCard)) { return false; } // state check - PersonCard card = (PersonCard) other; + ClientCard card = (ClientCard) other; return id.getText().equals(card.id.getText()) - && person.equals(card.person); + && client.equals(card.client); } } diff --git a/src/main/java/seedu/address/ui/ClientListPanel.java b/src/main/java/seedu/address/ui/ClientListPanel.java new file mode 100644 index 00000000000..1b64ffa52c2 --- /dev/null +++ b/src/main/java/seedu/address/ui/ClientListPanel.java @@ -0,0 +1,49 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.client.Client; + +/** + * Panel containing the list of clients. + */ +public class ClientListPanel extends UiPart { + private static final String FXML = "ClientListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(ClientListPanel.class); + + @FXML + private ListView clientListView; + + /** + * Creates a {@code ClientListPanel} with the given {@code ObservableList}. + */ + public ClientListPanel(ObservableList clientList) { + super(FXML); + clientListView.setItems(clientList); + clientListView.setCellFactory(listView -> new ClientListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Client} using a {@code ClientCard}. + */ + class ClientListViewCell extends ListCell { + @Override + protected void updateItem(Client client, boolean empty) { + super.updateItem(client, empty); + + if (empty || client == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new ClientCard(client, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java index 9a665915949..5ac69d5e68e 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/address/ui/HelpWindow.java @@ -15,7 +15,7 @@ */ public class HelpWindow extends UiPart { - public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html"; + public static final String USERGUIDE_URL = "https://ay2122s2-cs2103t-w13-1.github.io/tp/UserGuide.html"; public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 9106c3aa6e5..94aa1a69edf 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -2,12 +2,18 @@ import java.util.logging.Logger; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.MenuItem; import javafx.scene.control.TextInputControl; import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; +import javafx.scene.layout.ColumnConstraints; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.RowConstraints; import javafx.scene.layout.StackPane; import javafx.stage.Stage; import seedu.address.commons.core.GuiSettings; @@ -16,6 +22,8 @@ import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.client.Client; +import seedu.address.model.procedure.Procedure; /** * The Main Window. Provides the basic application layout containing @@ -31,7 +39,8 @@ public class MainWindow extends UiPart { private Logic logic; // Independent Ui parts residing in this Ui container - private PersonListPanel personListPanel; + private ProcedureListPanel procedureListPanel; + private ClientListPanel clientListPanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; @@ -42,7 +51,13 @@ public class MainWindow extends UiPart { private MenuItem helpMenuItem; @FXML - private StackPane personListPanelPlaceholder; + private GridPane clientListGridPane; + + @FXML + private StackPane clientListPanelPlaceholder; + + @FXML + private StackPane procedureListPanelPlaceholder; @FXML private StackPane resultDisplayPlaceholder; @@ -106,12 +121,52 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { }); } + /** + * Sets up the client column and procedure column of clientListGridPane. + */ + void setUpColumnConstraints() { + ColumnConstraints column1 = new ColumnConstraints(); + column1.setPercentWidth(50); + column1.setHgrow(Priority.ALWAYS); + ColumnConstraints column2 = new ColumnConstraints(); + column2.setPercentWidth(50); + column2.setHgrow(Priority.ALWAYS); + + RowConstraints row = new RowConstraints(); + row.setMaxHeight(Double.MAX_VALUE); + row.setVgrow(Priority.ALWAYS); + clientListGridPane.getColumnConstraints().addAll(column1, column2); // each get 50% of width + clientListGridPane.getRowConstraints().addAll(row); + } + + /** + * Adds the placeholder panes into the grid pane, + */ + void addPlaceholdersToGridPane() { + clientListGridPane.add(clientListPanelPlaceholder, 0, 0); + clientListGridPane.add(procedureListPanelPlaceholder, 1, 0); + } + /** * Fills up all the placeholders of this window. */ - void fillInnerParts() { - personListPanel = new PersonListPanel(logic.getFilteredPersonList()); - personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + void fillInnerParts() throws CommandException, ParseException { + setUpColumnConstraints(); + ObservableList clients = logic.getFilteredClientList(); + + clientListPanel = new ClientListPanel(clients); + clientListPanelPlaceholder.getChildren().add(clientListPanel.getRoot()); + + if (clients.size() > 0) { + procedureListPanel = new ProcedureListPanel( + FXCollections.observableArrayList(clients.get(0).getProcedures())); + logic.execute("listProc 1"); + } else { + procedureListPanel = new ProcedureListPanel(FXCollections.observableArrayList()); + } + procedureListPanelPlaceholder.getChildren().add(procedureListPanel.getRoot()); + + addPlaceholdersToGridPane(); resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); @@ -163,8 +218,8 @@ private void handleExit() { primaryStage.hide(); } - public PersonListPanel getPersonListPanel() { - return personListPanel; + public ClientListPanel getClientListPanel() { + return clientListPanel; } /** @@ -178,6 +233,10 @@ private CommandResult executeCommand(String commandText) throws CommandException logger.info("Result: " + commandResult.getFeedbackToUser()); resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()); + ObservableList filteredProcedures = logic.getFilteredProcedureList(); + procedureListPanel = new ProcedureListPanel(filteredProcedures); + procedureListPanelPlaceholder.getChildren().add(procedureListPanel.getRoot()); + if (commandResult.isShowHelp()) { handleHelp(); } diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java deleted file mode 100644 index f4c501a897b..00000000000 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ /dev/null @@ -1,49 +0,0 @@ -package seedu.address.ui; - -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.layout.Region; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; - -/** - * Panel containing the list of persons. - */ -public class PersonListPanel extends UiPart { - private static final String FXML = "PersonListPanel.fxml"; - private final Logger logger = LogsCenter.getLogger(PersonListPanel.class); - - @FXML - private ListView personListView; - - /** - * Creates a {@code PersonListPanel} with the given {@code ObservableList}. - */ - public PersonListPanel(ObservableList personList) { - super(FXML); - personListView.setItems(personList); - personListView.setCellFactory(listView -> new PersonListViewCell()); - } - - /** - * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}. - */ - class PersonListViewCell extends ListCell { - @Override - protected void updateItem(Person person, boolean empty) { - super.updateItem(person, empty); - - if (empty || person == null) { - setGraphic(null); - setText(null); - } else { - setGraphic(new PersonCard(person, getIndex() + 1).getRoot()); - } - } - } - -} diff --git a/src/main/java/seedu/address/ui/ProcedureCard.java b/src/main/java/seedu/address/ui/ProcedureCard.java new file mode 100644 index 00000000000..a5981117d96 --- /dev/null +++ b/src/main/java/seedu/address/ui/ProcedureCard.java @@ -0,0 +1,65 @@ +package seedu.address.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.model.procedure.Procedure; + +public class ProcedureCard extends UiPart { + private static final String FXML = "ProcedureListCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + public final Procedure procedure; + + @FXML + private HBox cardPane; + @FXML + private Label id; + @FXML + private Label information; + @FXML + private Label date; + @FXML + private Label cost; + @FXML + private Label hasCompleted; + + /** + * Creates a {@code ProcedureCard} with the given {@code Procedure} and index to display. + */ + public ProcedureCard(Procedure procedure, int displayedIndex) { + super(FXML); + this.procedure = procedure; + id.setText(displayedIndex + ". "); + information.setText(procedure.getInfo().toString()); + date.setText("Date: " + procedure.getDate().toString()); + cost.setText("Cost: $" + procedure.getCost().toString()); + hasCompleted.setText("Completed: " + procedure.getHasCompleted().toString()); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ProcedureCard)) { + return false; + } + + // state check + ProcedureCard card = (ProcedureCard) other; + return id.getText().equals(card.id.getText()) + && procedure.equals(card.procedure); + } +} diff --git a/src/main/java/seedu/address/ui/ProcedureListPanel.java b/src/main/java/seedu/address/ui/ProcedureListPanel.java new file mode 100644 index 00000000000..828d3d09f1e --- /dev/null +++ b/src/main/java/seedu/address/ui/ProcedureListPanel.java @@ -0,0 +1,45 @@ +package seedu.address.ui; +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.procedure.Procedure; + + +public class ProcedureListPanel extends UiPart { + private static final String FXML = "ProcedureListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(ProcedureListPanel.class); + + @FXML + private ListView procedureListView; + + /** + * Creates a {@code ProcedureListPanel} with the given {@code ObservableList}. + */ + public ProcedureListPanel(ObservableList procedureList) { + super(FXML); + procedureListView.setItems(procedureList); + procedureListView.setCellFactory(listView -> new ProcedureListViewCell()); + procedureListView.setStyle("-fx-border-width: 0 0 0 2; -fx-border-color: white;"); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Procedure} using a {@code ProcedureCard}. + */ + class ProcedureListViewCell extends ListCell { + @Override + protected void updateItem(Procedure procedure, boolean empty) { + super.updateItem(procedure, empty); + if (empty || procedure == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new ProcedureCard(procedure, getIndex() + 1).getRoot()); + } + } + } +} diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/address/ui/UiManager.java index fdf024138bc..124e173b76d 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/address/ui/UiManager.java @@ -20,7 +20,7 @@ public class UiManager implements Ui { public static final String ALERT_DIALOG_PANE_FIELD_ID = "alertDialogPane"; private static final Logger logger = LogsCenter.getLogger(UiManager.class); - private static final String ICON_APPLICATION = "/images/address_book_32.png"; + private static final String ICON_APPLICATION = "/images/networkers_logo.png"; private Logic logic; private MainWindow mainWindow; @@ -43,7 +43,6 @@ public void start(Stage primaryStage) { mainWindow = new MainWindow(primaryStage, logic); mainWindow.show(); //This should be called before creating other UI parts mainWindow.fillInnerParts(); - } catch (Throwable e) { logger.severe(StringUtil.getDetails(e)); showFatalErrorDialogAndShutdown("Fatal error during initializing", e); diff --git a/src/main/resources/images/networkers_logo.png b/src/main/resources/images/networkers_logo.png new file mode 100644 index 00000000000..056831b6752 Binary files /dev/null and b/src/main/resources/images/networkers_logo.png differ diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/ClientListCard.fxml similarity index 95% rename from src/main/resources/view/PersonListCard.fxml rename to src/main/resources/view/ClientListCard.fxml index f08ea32ad55..75234160fb3 100644 --- a/src/main/resources/view/PersonListCard.fxml +++ b/src/main/resources/view/ClientListCard.fxml @@ -28,6 +28,7 @@