diff --git a/README.md b/README.md index 13f5c77403f..dac16d41301 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) +[![CI Status](https://github.com/AY2324S2-CS2103T-W10-2/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2324S2-CS2103T-W10-2/tp/actions) ![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. +## PoochPlanner +This is an **address book for dog cafe owners**.
+Example usages: +* as an address book to store contacts + +The project simulates an ongoing software project for a desktop application (called _PoochPlanner_) 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. +* It is named `PoochPlanner` because it is meant for pooch (dog) cafe owners to manage their address books. +* For the detailed documentation of this project, see **[PoochPlanner's Product Website](https://ay2324s2-cs2103t-w10-2.github.io/tp/)**. + +This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). diff --git a/bin/test/seedu/address/testutil/EditSupplierDescriptorBuilder.class b/bin/test/seedu/address/testutil/EditSupplierDescriptorBuilder.class new file mode 100644 index 00000000000..772e6bbb43f Binary files /dev/null and b/bin/test/seedu/address/testutil/EditSupplierDescriptorBuilder.class differ diff --git a/build.gradle b/build.gradle index a2951cc709e..98c44b0dc9b 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,10 @@ checkstyle { toolVersion = '10.2' } +run { + enableAssertions = true +} + test { useJUnitPlatform() finalizedBy jacocoTestReport diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..22d9741fac4 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -5,55 +5,53 @@ 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` - ## Project team -### John Doe +### Lee Yi Lin - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/yleeyilin)] +[[portfolio](team/yleeyilin.md)] -* Role: Project Advisor +* Role: Developer +* Responsibilities: Code Quality -### Jane Doe +### James Sin - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/jamessinmaojun)] +[[portfolio](team/jamessinmaojun.md)] -* Role: Team Lead -* Responsibilities: UI +* Role: Developer +* Responsibilities: Documentation -### Johnny Doe +### Leong Jia Yi, Janna - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](http://github.com/jannaleong)] [[portfolio](team/jannaleong.md)] -* Role: Developer -* Responsibilities: Data +* Role: Team Lead +* Responsibilities: Overall project coordination, deliverables and deadlines -### Jean Doe +### Joshua Yip Sujun - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/joshy837)] +[[portfolio](team/joshy837.md)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: Testing -### James Doe +### Chng Chia Geng - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/chiageng)] +[[portfolio](team/chiageng.md)] * Role: Developer -* Responsibilities: UI +* Responsibilities: Integration diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 1b56bb5d31b..a450fd4ceba 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -5,13 +5,7 @@ title: Developer Guide * Table of Contents {:toc} --------------------------------------------------------------------------------------------------------------------- - -## **Acknowledgements** - -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} - --------------------------------------------------------------------------------------------------------------------- +
## **Setting up, getting started** @@ -23,143 +17,497 @@ Refer to the guide [_Setting up and getting started_](SettingUp.md).
-:bulb: **Tip:** The `.puml` files used to create diagrams in this document `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 are in this document `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 -The ***Architecture Diagram*** given above explains the high-level design of the App. +The ***Architecture Diagram*** given above explains the high-level design of the application. -Given below is a quick overview of main components and how they interact with each other. +Below is a quick overview of the main components and how they interact with each other.

**Main components of the architecture** -**`Main`** (consisting of classes [`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)) is in charge of the app launch and shut down. -* At app launch, it initializes the other components in the correct sequence, and connects them up with each other. -* At shut down, it shuts down the other components and invokes cleanup methods where necessary. +**`Main`** (consisting of the classes [`Main`](https://github.com/AY2324S2-CS2103T-W10-2/tp/blob/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/AY2324S2-CS2103T-W10-2/tp/blob/master/src/main/java/seedu/address/MainApp.java)) is in charge of the application launch and shut down. +* On application launch, it initializes the other components in the correct sequence, and connects them up with each other. +* On shut down, it shuts down the other components and invokes cleanup methods where necessary. -The bulk of the app's work is done by the following four components: +The bulk of the application's work is done by the following four components: -* [**`UI`**](#ui-component): The UI of the App. +* [**`UI`**](#ui-component): The User Interface of the application. * [**`Logic`**](#logic-component): The command executor. -* [**`Model`**](#model-component): Holds the data of the App in memory. +* [**`Model`**](#model-component): Holds the application data in memory. * [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk. -[**`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.

**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 architecture components interact with one another for the scenario where the user issues the command `/delete ; name : Poochie`: + + - +Each of the four main components (also shown in the diagram above): -Each of the four main components (also shown in the diagram above), +* Defines its *API* in an `interface` with the same name as the component. +* Implements its functionality using a concrete `{Component Name} Manager` class which follows the corresponding API interface mentioned in the previous point. -* defines its *API* in an `interface` with the same name as the Component. -* implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point. -For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below. +For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside components from being coupled to the implementation of a component), as illustrated in the (partial) class diagram below: -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) +The ***API*** of this component is specified in [`Ui.java`](https://github.com/AY2324S2-CS2103T-W10-2/tp/blob/master/src/main/java/seedu/address/ui/Ui.java). + +Below is a class diagram of the `UI` component: + +
![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` 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 consists of a `MainWindow` component which itself is made up of sub-components e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these UI components, including the `MainWindow` component, 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 located in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/AY2324S2-CS2103T-W10-2/tp/blob/master/src/main/java/seedu/address/ui/MainWindow.java) component is specified in [`MainWindow.fxml`](https://github.com/AY2324S2-CS2103T-W10-2/tp/blob/master/src/main/resources/view/MainWindow.fxml) -The `UI` component, +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`. +* 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` component relies on the `Logic` component to execute commands. +* Depends on some classes in the `Model` component, as it displays the `Person` object 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) +The ***API*** of this component is specified in [`Logic.java`](https://github.com/AY2324S2-CS2103T-W10-2/tp/blob/master/src/main/java/seedu/address/logic/Logic.java). + +Below is a (partial) class diagram of the `Logic` component: -Here's a (partial) class diagram of the `Logic` component: +
-The sequence diagram below illustrates the interactions within the `Logic` component, taking `execute("delete 1")` API call as an example. +The sequence diagram below illustrates the interactions within the `Logic` component, taking `execute("/delete ; name : Poochie")` API call as an example. -![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) +![Interactions Inside the Logic Component for the `/delete ; name : Poochie` 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 continues till the end of diagram. +
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of the diagram.
-How the `Logic` component works: +Execution lifecycle of the `Logic` component: -1. When `Logic` is called upon to execute a command, it is passed to an `AddressBookParser` object which in turn creates a parser that matches the command (e.g., `DeleteCommandParser`) and uses it to parse the command. -1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `DeleteCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to delete a person).
+1. When `Logic` is called upon to execute a command, it is passed to an `AddressBookParser` object which in turn creates a parser that matches the command (e.g. `DeleteCommandParser`) and uses it to parse the command. +2. This results in a `Command` object (more precisely, an object of one of its subclasses e.g. `DeleteCommand`) which is executed by the `LogicManager`. +3. The command can communicate with the `Model` when it is executed (e.g. to delete a person).
Note that although this is shown as a single step in the diagram above (for simplicity), in the code it can take several interactions (between the command object and the `Model`) to achieve. -1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. +4. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. -Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command: +Illustrated below are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command: How the parsing works: -* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object. -* 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. +1. When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g. `AddCommandParser`). `XYZCommandParser` uses the other classes as shown above to parse the user command and creates an `XYZCommand` object (e.g. `AddCommand`) which the `AddressBookParser` returns back as a `Command` object. +2. All `XYZCommandParser` classes (e.g. `AddCommandParser`, `DeleteCommandParser` etc.) 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) - +The ***API*** of the `Model` component is specified in [`Model.java`](https://github.com/AY2324S2-CS2103T-W10-2/tp/blob/master/src/main/java/seedu/address/model/Model.java). +Below is a class diagram of the `Model` component: -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 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.
+The `Model` component: - - -
+* Stores different states of `AddressBook` inside `VersionedAddressBook`. +* Stores all data from PoochPlanner (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` object 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 changes). +* Stores a `UserPref` object that represents the user’s preferences. This is exposed to outsiders as a `ReadOnlyUserPref` object. +* 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). +
### Storage component -**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) +The ***API*** of this component is specified in [`Storage.java`](https://github.com/AY2324S2-CS2103T-W10-2/tp/blob/master/src/main/java/seedu/address/storage/Storage.java). + +Below is a class diagram of the `Storage` component: + +
- + -The `Storage` component, -* can save both address book data and user preference data in JSON format, and read them back into corresponding objects. -* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). -* depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`) +The `Storage` component: + +* Saves both PoochPlanner data and user preference data in JSON format, which is read during the bootup of PoochPlanner. +* Inherits from both `AddressBookStorage` and `UserPrefStorage` interfaces, which means that it can be treated as either one (if the functionality of only one interface is needed). +* Depends on some classes in the `Model` component (because the `Storage` component's job is to save and retrieve objects that belong to the `Model`). ### Common classes Classes used by multiple components are in the `seedu.addressbook.commons` package. --------------------------------------------------------------------------------------------------------------------- +
## **Implementation** This section describes some noteworthy details on how certain features are implemented. -### \[Proposed\] Undo/redo feature +[//]: # (@@author chiageng) +### Add feature + +#### Overview + +The `add-XYZ` command enables users to add a new contact to PoochPlanner. + +The following sequence diagram models the interactions between the different components of PoochPlanner for the execution of the `add-person` command: + +![Add Sequence Diagram](images/AddCommandSequenceDiagram.png) + +
:information_source: **Note:** The implementation for adding `Person`, `Staff`, `Supplier`, and `Maintainer` are similar and only differ in their accepted attributes. `XYZ` can refer to either `person`, `staff`, `supplier`, or `maintainer`. +
+ +#### Details + +1. The user inputs the command to add a new contact. +2. An `AddCommandParser` object invokes its `parse` method which parses the user input. +3. An `AddCommand` object is created. +4. The `AddCommandParser` object returns the `AddCommand` object. +5. A `LogicManager` object invokes the `execute` method of the `AddCommand` object. +6. The `execute` method of the `AddCommand` object invokes the `addPerson` method of its `Model` argument to create a new contact with a new `Person` object. +7. The `execute` method of the `AddCommand` object returns a `CommandResult` object which stores the data regarding the completion of the `add-XYZ` command. + +#### Example Usage +1. The user launches the application. +2. The user inputs `/add-person ; name : John Doe ; phone : 98765432 ; email : johnd@example.com ; address : 311, Clementi Ave 2, #02-25` into the CLI. +3. The contact card for the person named "John Doe" is created. This change should be reflected in the contacts list in PoochPlanner. + +[//]: # (@@author yleeyilin) + +
+ +### Edit feature + +#### Overview + +The `edit-XYZ` command enables users to modify specified field(s) of an existing contact in PoochPlanner. + +The following sequence diagram models the interactions between the different components of PoochPlanner for the execution of the `edit-XYZ` command: + +![Edit Sequence Diagram](images/EditCommandSequenceDiagram.png) + +
:information_source: **Note:** The implementation for editing a `Person`, `Staff`, `Supplier`, and `Maintainer` are similar and only differ in their accepted attributes. `XYZ` can refer to either `person`, `staff`, `supplier`, or `maintainer`. +
+ +#### Details + +1. The user inputs the command to edit an existing contact by first stating the name of the contact they want to edit. This is followed by specifying the respective fields and new values that the user wants to modify. +2. An `EditCommandParser` object invokes its `parse` method which parses the user input and creates an `EditPersonDescriptor` object which contains the new values to be edited for the specified contact. +3. An `EditCommand` object is created with the name of the contact to edit and the `EditPersonDescriptor` object. +4. The `EditCommandParser` returns the `EditCommand` object. +5. A `LogicManager` object invokes the `execute` method of the `EditCommand` object. +6. The `execute` method of the `EditCommand` objects finds the specified contact by its name. The `execute` method then calls the `createEditedPerson` method of the `EditCommand` object which creates a new `Person` object that contains the updated values of the contact. +7. The `execute` method of the `EditCommand` object invokes the `setPerson` method of its `Model` argument to replace the specified contact with the new `Person` object. +8. The `execute` method of the `EditCommand` object invokes the `updateFilteredPersonList` method of its `Model` argument to update the view of PoochPlanner to show all contacts. +9. The `execute` method of the `EditCommand` object returns a `CommandResult` object which stores the data regarding the completion of the `edit-XYZ` command. + +#### Example Usage +1. The user launches the application. +2. The user inputs `/edit-person ; name : Alice Tan ; field : { phone : 9990520 ; email : impooch@gmail12.com }` into the CLI. +3. The contact card for the person named "Alice Tan" has its `phone` and `email` fields updated respectively. This change should be reflected in the contacts list in PoochPlanner. + +**Aspect: How to implement the edit command** + +* **Alternative 1 (current choice)**: Create four distinct edit commands for the four contact types (`Person`, `Staff`, `Maintainer`, `Supplier`). + * Pros: More user-friendly since users will be less prone to error that involves trying to edit a field that does not exist for the specific contact type. + * Cons: Steeper learning curve for users due to the greater number of commands. + +* **Alternative 2**: Use only one edit command across all classes by using a dynamic edit parser. The dynamic edit parser will internally route to the correct edit command to handle the modification of different contact types separately. + * Pros: Much simpler suite of features for users, which makes it easier for users to start using PoochPlanner. + * Cons: Complex to implement since the checking of the contact type must be done at the point of parsing by the dynamic edit parser. However, doing so will violate the intended abstracted implementation of MVC (Model-View-Controller) as the model will have to be accessible from within the parser class in order for the type checking to be done. + +
+ +### Search feature + +#### Overview + +The `search` command enables users to find contacts in PoochPlanner that match the input search query. + +The following sequence diagram models the interactions between the different components of PoochPlanner for the execution of the `search` command: + + + +#### Details + +1. The user inputs the command to search for contacts with the specified search query. +2. A `SearchCommandParser` object invokes its `parse` method which parses the user input by storing the prefixes and their respective values in an `ArgumentMultimap` object, and using this object to create an instance of `KeywordPredicate`. +3. The `SearchCommandParser` object then creates a `SearchCommand` object containing the aforementioned `KeywordPredicate` object. +4. A `LogicManager` object invokes the `execute` method of the `SearchCommand` object. +5. The `execute` method of the `SearchCommand` object invokes the `updateFilteredPersonList` method of its `Model` argument, taking in the `KeywordPredicate` object as a parameter to filter and update the view of PoochPlanner. +6. The `execute` method of the `SearchCommand` object returns a `CommandResult` object which stores the data regarding the completion of the `search` command. + +#### Example Usage + +1. The user launches the application. +2. The user inputs `/search ; name : Poochie` into the CLI. +3. PoochPlanner is updated to display all contact cards with contacts containing "Poochie" in their name. + +**Aspect: How to implement the search command** + +* **Alternative 1 (current choice)**: Accept multiple search fields in the search query. + * Pros: More user-friendly as users can search using multiple fields at once, allowing for a more targeted search. + * Cons: More prone to errors due to the broader search scope over multiple fields. + +* **Alternative 2**: Only accept one field in the search query. + * Pros: Less prone to errors due to the stricter search only over one field. + * Cons: Less user-friendly since users will not be able to search using multiple fields at once. + +
+ +### Delete feature + +#### Overview + +The `delete` command enables users to delete a specific contact from PoochPlanner. + +The following sequence diagram models the interactions between the different components of PoochPlanner for the +execution of the `delete` command: + + + +#### Details + +1. The user inputs the command to delete a contact by stating the name of the contact they want to delete. +2. A `DeleteCommandParser` object invokes its `parse` method which parses the user input by storing the prefixes and their respective values as an `ArgumentMultimap` object. +3. A `DeleteCommand` object is created with the name of the contact to delete. +4. The `DeleteCommandParser` object returns the `DeleteCommand` object. +5. A `LogicManager` object invokes the `execute` method of the `DeleteCommand` object. +6. The `execute` method of the `DeleteCommand` object invokes the `deletePerson` method of its `Model` argument which removes the specified contact from its `addressBook` property. +7. The `execute` method of `DeleteCommand` returns a `CommandResult` object which stores the data regarding the completion of the `delete` command. + +#### Example Usage + +1. The user launches the application. +2. The user inputs `/delete ; name : Poochie` into the CLI. +3. The contact with the name "Poochie" will be deleted from PoochPlanner. This change should be reflected in the contacts list in PoochPlanner. + +**Aspect: How to implement delete command** + +* **Alternative 1 (current choice)**: Accept multiple name fields, where only the last name field will be taken. + * Pros: More user-friendly. Should users make a mistake, they can easily append another name field without deleting the previous name field entered. + * Cons: Less rigorous validation check on entered names as users may not intentionally enter a second name field. + +* **Alternative 2**: Accept only one name field. + * Pros: Less prone to possible errors due to stricter validation checks on name fields. + * Cons: Less user-friendly since users will have to put in more effort to fix their commands. + +
+ +### Rate feature + +#### Overview + +The `rate` command enables users to rate a specific contact in PoochPlanner. + +The following sequence diagram models the interactions between the different components of PoochPlanner for the +execution of the `rate` command: + + + +#### Details + +1. The user inputs the command to add a rating to a specific contact by first stating the name of the + contact they want to rate. This is followed by the rating to be given to the user. +2. A `RateCommandParser` object invokes its `parse` method which parses the user input by storing the name and its + prefix in an `ArgumentMultimap` object. +3. A `RateCommand` object is created with the parsed name and rating. +4. A `LogicManager` object invokes the `execute` method of the `RateCommand` object. +5. The `execute` method of the `RateCommand` object invokes the `findByName` method of its `Model` argument to find the + contact with the specified name. +6. The `execute` method of the `RateCommand` object invokes the `setPerson` method of its `Model` argument to set the contact + in the existing contacts list to the new `Person` object which has been edited by the `execute` method of the `RateCommand` object. +7. The `execute` method of the `RateCommand` object invokes the `updateFilteredPersonList` method of its `Model` argument to + update the view of PoochPlanner to show all contacts. +8. The `execute` method of the `RateCommand` objects returns a `CommandResult` object which stores the data regarding the + completion of the `rate` command. + +#### Example Usage + +1. The user launches the application. +2. The user inputs `/rate ; name : Poochie ; rating : 5` into the CLI. +3. The contact with the name "Poochie" is given a rating of "5". This change should be reflected in the contacts list in PoochPlanner. + +**Aspect: How to store rating field in Person class and subclasses** + +* **Alternative 1 (current choice)**: Add the rating field to all four constructors (`Person`, `Staff`, `Maintainer`, `Supplier`). + * Pros: Leverages inheritance, thus reducing repeated code and adheres to OOP. + * Cons: Changing the constructors of the four classes is a tedious task. + +* **Alternative 2**: Add the rating field to the parent person constructor and use a setter to set new ratings. + * Pros: Much simpler implementation that will require less refactoring of code. + * Cons: Violates OOP, specifically encapsulation as the other classes would be able to manipulate the + ratings of `Person` objects directly. + +
+ +[//]: # (@@author yleeyilin) + +### Pin and Unpin features + +#### Overview + +The `pin` and `unpin` commands enable users to pin and unpin any existing contacts in PoochPlanner. + +The following sequence diagram models the interactions between the different components of PoochPlanner for the execution of the `pin` command: + +![Pin Sequence Diagram](images/PinCommandSequenceDiagram.png) + +The following sequence diagram models the interactions between the different components of PoochPlanner for the execution of the `unpin` command: + +![Unpin Sequence Diagram](images/UnpinCommandSequenceDiagram.png) + +
:information_source: **Note:** The implementation for pinning and unpinning `Person`, `Staff`, `Supplier`, `Maintainer` are the same. The `pin` and `unpin` commands are also implemented similarly as seen in the sequence diagrams above. +
+ +#### Details + +1. The user inputs the command to pin/unpin a specified contact by stating the target name of the contact that they want to pin/unpin. +2. A `PinCommandParser`/`UnpinCommandParser` object invokes its `parse` method which parses the user input by storing the prefixes and their respective values as an `ArgumentMultimap` object. +3. A `PinCommand`/`UnpinCommand` object is created with the name of the contact to pin/unpin. +4. The `PinCommandParser`/`UnpinCommandParser` object returns the `PinCommand`/`UnpinCommand` object. +5. A `LogicManager` object invokes the `execute` method of the `PinCommand`/`UnpinCommand` object. +6. The `execute` method of the `PinCommand`/`UnpinCommand` object finds the specified contact by its name. The `updateToPinned`/`updateToUnpinned` method of the found `Person` object creates a new `Person` object that contains the updated `pin` boolean of the contact. +7. The `execute` method of the `PinCommand`/`UnpinCommand` object invokes the `setPerson` method of its `Model` argument to replace the specified contact with the new `Person` object. +8. The `execute` method of the `PinCommand`/`UnpinCommand` object invokes the `updatePinnedPersonList` method of its `Model` argument to update the view of PoochPlanner to show all contacts. +9. The `execute` method of the `PinCommand`/`UnpinCommand` object returns a `CommandResult` object which stores the data regarding the completion of the `pin`/`unpin` command. + +#### Example Usage + +1. The user launches the application. +2. The user inputs `/pin ; name : Alice Tan` or `/unpin ; name : Alice Tan` into the CLI. +3. The contact card for the contact named "Alice Tan" is now pinned/unpinned. This change should be reflected in the contacts list in PoochPlanner. + +**Aspect: How to implement pin/unpin command** + +* **Alternative 1 (current choice)**: Accept multiple name fields, where only the last name field will be taken. + * Pros: More user-friendly. Should users make a mistake, they can easily append another name field without deleting the previous name field entered. + * Cons: Less rigorous validation check on name as users may not intentionally enter a second name field. + +* **Alternative 2**: Accept only one name field. + * Pros: Less prone to possible errors due to stricter validation checks on name fields. + * Cons: Less user-friendly since users will have to put in more effort to fix their commands. + +
+ +### Sort feature + +#### Overview + +The `sort` command enables users to sort contacts in PoochPlanner by a contact field. + +The following sequence diagram models the interactions between the different components of PoochPlanner for the execution of the `sort` command: + + + +#### Details + +1. The user inputs the command to sort contacts with the target field. +2. A `SortCommandParser` object invokes its `parse` method which parses the user input through `ArgumentMultimap` and `mapName`, creating a new `Prefix` object. +3. The `SortCommandParser` object then creates a new `SortCommand` object with the target `prefix`, returning this object. +4. A `LogicManager` object invokes the `execute` method of the `SortCommand` object. +5. The `execute` method of the `SortCommand` object invokes the `updateSortedPersonList` method of its `Model` argument with the target `prefix` to update the view of PoochPlanner to sort all contacts by the target field. +6. The `execute` method of the `SortCommand` object returns a `CommandResult` object which stores the data regarding the completion of the `sort` command. + +#### Example Usage + +1. The user launches the application. +2. The user inputs `/sort ; field : phone` into the CLI. +3. PoochPlanner is updated to sort all the contact cards by phone number in ascending order. This change should be reflected in the contacts list in PoochPlanner. + +**Aspect: How to implement sort command** + +* **Alternative 1 (current choice)**: Sorts only by ascending order, alphabetically and numerically (except ratings, which are sorted in descending order). + * Pros: Straightforward to input command. Users can just key in the field they want to sort by without having to indicate whether to sort either in ascending or descending order. + * Cons: Less flexible in sorting as later alphabets and larger values will take longer to find. + +* **Alternative 2**: Sorts by both ascending and descending order depending on user indication. + * Pros: More flexible in sorting for users to sort in either way to find what they need. + * Cons: Longer command, another field required to specify either ascending or descending sorting order. + +
+ +[//]: # (@@author jannaleong) + +### Note feature + +#### Overview -#### Proposed Implementation +The `note` command enables users to add notes to existing contacts in PoochPlanner. -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: +The following sequence diagram models the interactions between the different components of PoochPlanner for the execution of the `note` command. + + + +#### Details + +1. The user inputs the command to add a note to a specified contact by first stating the name of the contact they want to add a note to. This is followed by the note and an optional deadline prefixes and their respective values. +2. A `NoteCommandParser` object invokes its `parse` method which parses the user input by storing the prefixes and their respective values as an `ArgumentMultimap` object. +3. A `NoteCommand` object is created with the parsed name, note and optional deadline field. +4. The `NoteCommandParser` object returns the `NoteCommand` object. +5. A `LogicManager` object invokes the `execute` method of the `NoteCommand` object. +6. The `execute` method of the `NoteCommand` object invokes the `findByName` method of its `Model` argument to find the person with the specified name. +7. The `execute` method of the `NoteCommand` object invokes the `setPerson` method of its `Model` argument to set the person in the existing contacts list to the new `Person` object which has been edited by the `execute` method of the `NoteCommand` object. +8. The `execute` method of the `NoteCommand` object invokes the `updateFilteredPersonList` method of its `Model` argument to update the view of PoochPlanner to show all contacts. +9. The `execute` method of the `NoteCommand` object returns a `CommandResult` object which stores the data regarding the completion of the `note` command. + +#### Example Usage + +1. The user launches the application. +2. The user inputs `/note ; name : Janna ; note : get kibble` into the CLI. +3. The given note will be added to the description of the contact with the given name. This change should be reflected in the contacts list in PoochPlanner. + +**Aspect: How to store note field in Person class and subclasses** + +* **Alternative 1 (current choice)**: Add note field to all four constructors (`Person`, `Staff`, `Maintainer`, `Supplier`). + * Pros: Leverages inheritance, thus reducing repeated code and adheres to OOP. + * Cons: Changing the constructors of the four classes is a tedious task. + +* **Alternative 2**: Add note field to the parent person constructor and use a setter to set new notes. + * Pros: Much simpler implementation that will require less refactoring of code. + * Cons: Violates OOP, specifically encapsulation as the other classes would be able to manipulate the + inner details of `Person` objects directly. + +
+ +[//]: # (@@author) + +[//]: # (@@author chiageng) +### Undo and redo features + +#### Overview + +The `undo` and `redo` commands enable users to undo and redo previous actions made in PoochPlanner. + +
+ +As the implementation of the `undo` and `redo` commands are more complicated, the mechanism is fully explained with the corresponding implementation as described below. + +#### Implementation + +The undo/redo mechanism is facilitated by the `VersionedAddressBook` class. It extends the `AddressBook` class with an additional undo/redo history, stored internally with the `addressBookStateList` and `currentStatePointer` properties. Additionally, it supports the following operations: * `VersionedAddressBook#commit()` — Saves the current address book state in its history. * `VersionedAddressBook#undo()` — Restores the previous address book state from its history. @@ -169,28 +517,28 @@ These operations are exposed in the `Model` interface as `Model#commitAddressBoo Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. -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 1. The user launches the application for the first time. A `VersionedAddressBook` object will be initialized with an initial AddressBook state, with the `currentStatePointer` pointer pointing to that single address book state. ![UndoRedoState0](images/UndoRedoState0.png) -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 2. The user executes the `/delete ; name : Poochie` command to delete the contact named "Poochie" from PoochPlanner. The `delete` command calls the `Model#commitAddressBook()` method, causing the modified state of the address book after the `/delete ; name : Poochie` command executes to be saved in the `addressBookStateList` property, and the `currentStatePointer` pointer is shifted to the newly inserted address book state. ![UndoRedoState1](images/UndoRedoState1.png) -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 3. The user executes `/add-person ; name : John …​` to add a new contact named "John". The `add-person` command also calls the `Model#commitAddressBook()` method, causing another modified address book state to be saved into the `addressBookStateList` property. ![UndoRedoState2](images/UndoRedoState2.png) -
: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`. +
:information_source: **Note:** If a command fails during its execution, it will not call the `Model#commitAddressBook()` method, and consequently the address book state will not be saved into the `addressBookStateList` property.
-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. +Step 4. The user now decides that adding John was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call the `Model#undoAddressBook()` method, which will shift the `currentStatePointer` pointer once to the left, pointing it to the previous address book state, and restores the address book to that state. ![UndoRedoState3](images/UndoRedoState3.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. +
:information_source: **Note:** If the `currentStatePointer` pointer is at index 0 (pointing to the initial address book state), then there are no previous address book states to restore. The `undo` command calls the `Model#canUndoAddressBook()` method to check if this is the case. If so, it will return an error to the user rather +than attempt to perform the undo.
@@ -198,7 +546,7 @@ The following sequence diagram shows how an undo operation goes through the `Log ![UndoSequenceDiagram](images/UndoSequenceDiagram-Logic.png) -
: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. +
:information_source: **Note:** The lifeline for the `UndoCommand` object should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
@@ -206,17 +554,17 @@ Similarly, how an undo operation goes through the `Model` component is shown bel ![UndoSequenceDiagram](images/UndoSequenceDiagram-Model.png) -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. +The `redo` command does the opposite — it calls the `Model#redoAddressBook()` method, which shifts the `currentStatePointer` pointer once to the right, pointing to the previously undone state, and restores the address book to that state. -
: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. +
:information_source: **Note:** If the `currentStatePointer` pointer is at index `addressBookStateList.size() - 1` (pointing to the latest address book state), then there are no undone address book states to restore. The `redo` command calls the `Model#canRedoAddressBook()` method to check if this is the case. If so, it will return an error to the user rather than attempt to perform the redo.
-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 5. The user then decides to execute the `list` command. Commands that do not modify the address book, such as `list`, will not call the `Model#commitAddressBook()` method. Thus, the `addressBookStateList` property remains unchanged. ![UndoRedoState4](images/UndoRedoState4.png) -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 6. The user executes the `clear` command, which calls the `Model#commitAddressBook()` method. Since the `currentStatePointer` pointer is not pointing at the end of the `addressBookStateList` property, all address book states after the `currentStatePointer` property will be purged (reason: It no longer makes sense to redo the `/add-person ; name : John...` command). This is the behavior that most modern desktop applications follow. ![UndoRedoState5](images/UndoRedoState5.png) @@ -224,27 +572,135 @@ The following activity diagram summarizes what happens when a user executes a ne -#### Design considerations: +### Design considerations: -**Aspect: How undo & redo executes:** +#### Aspect: How undo and redo executes: -* **Alternative 1 (current choice):** Saves the entire address book. +**Alternative 1 (current choice)**: Save snapshots of the entire address book in individual states (store a revision history). * Pros: Easy to implement. * Cons: May have performance issues in terms of memory usage. -* **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. +**Alternative 2**: Implement inverse commands (commands that are antagonistic in nature). +* Pros: Significant reduction in memory overhead. +* Cons: Not all commands have an inverse (e.g. the `sort` command is not a bijection and hence no single function exists as its inverse). -_{more aspects and alternatives to be added}_ +[//]: # (@@author jannaleong) -### \[Proposed\] Data archiving +### Help feature -_{Explain here how the data archiving feature will be implemented}_ +#### Overview +The `help` command enables users to view help for all commands. --------------------------------------------------------------------------------------------------------------------- +The following sequence diagram models the interactions between the different components of PoochPlanner for the execution of the `help` command: + + + +#### Details + +1. The user inputs the command to view help for a specific command. This is followed by the command field specifying the command they want to view help for. +2. A `HelpCommandParser` object invokes its `parse` method which parses the user input by storing the prefix of its respective values as an `ArgumentMultimap` object. +3. A `HelpCommand` object is created with the command type that was specified in the command field. +4. The `HelpCommandParser` object returns the `HelpCommand` object. +5. A `LogicManager` object invokes the `execute` method of the `HelpCommand` object. +6. The `execute` method of the `HelpCommand` object returns a `CommandResult` object which stores the data regarding the completion of the `help` command. + +#### Example Usage + +1. The user launches the application. +2. The user inputs `/help ; command : delete` into the CLI. +3. Help for the `delete` command will be displayed. + +**Aspect: How to display different help command windows** + +* **Alternative 1 (current choice)**: Use only one help window class to display different help messages for different commands. Different content is displayed by passing in different strings. + * Pros: Code is made much more concise. + * Cons: Lengthy if-else statements are required to display the correct string. + +* **Alternative 2**: Create a different help window class for each type of command. + * Pros: All details relating to a single command is within its own file. Can be perceived as neater. + * Cons: Highly repetitive code. Even small mistakes made would have to be fixed in over ten windows. + +
+ +### Remind feature + +#### Overview + +The `remind` command enables users to view all contacts with note deadlines from today onwards. + +The following sequence diagram models the interactions between the different components of PoochPlanner for the execution of the `remind` command: + +![Remind Sequence Diagram](images/RemindCommandSequenceDiagram.png) + +#### Details + +1. The user inputs the command to view reminders. +2. A `RemindCommand` object is created. +3. A `LogicManager` object invokes the `execute` method of the `RemindCommand` object. +4. The `execute` method of the `RemindCommand` object invokes the `updateFilteredPersonList` method of its `Model` argument to update the view of the application to show contacts + with note deadlines from today onwards. +5. The `execute` method of the `RemindCommand` object returns a `CommandResult` object which stores the data regarding the completion of the `remind` command. + +#### Example Usage + +1. The user launches the application. +2. The user inputs `/remind` into the CLI. +3. Contacts that have deadline notes from today onwards will be displayed. + +
+ +### Clear feature + +#### Overview + +The `clear` command enables users to remove all existing contacts from PoochPlanner. + +The following sequence diagram models the interactions between the different components of PoochPlanner for the execution of the `clear` command: + +![Clear Sequence Diagram](images/ClearCommandSequenceDiagram.png) + +#### Details + +1. The user inputs the command to clear all contacts. +2. A `LogicManager` object invokes the `execute` method of a `ClearCommand` object. +3. The `execute` method of the `ClearCommand` object invokes the `setAddressBook` method of its `Model` argument with a new `AddressBook` object which contains an empty `UniquePersonList` property. +4. The `execute` method of the `ClearCommand` object returns a `CommandResult` object which stores the data regarding the completion of the `clear` command. + +#### Example Usage + +1. The user launches the application. +2. The user inputs `/clear` into the CLI. +3. The data in PoochPlanner is emptied. + +
+ +### List feature + +#### Overview + +The `list` command enables users to view all existing contacts from PoochPlanner. + +The following sequence diagram models the interactions between the different components of PoochPlanner for the execution of the `list` command: + +![List Sequence Diagram](images/ListCommandSequenceDiagram.png) + +#### Details + +1. The user inputs the command to list all contacts. +2. A `LogicManager` object invokes the `execute` method of a `ListCommand` object. +3. The `execute` method of the `ListCommand` object invokes the `updateFilteredPersonList` method of its `Model` argument to update the view of the application to show all contacts. +4. The `execute` method of the `ListCommand` object returns a `CommandResult` object which stores the data regarding the completion of the `list` command. + +#### Example Usage + +1. The user launches the application. +2. The user inputs `/list` into the CLI. +3. All contacts in PoochPlanner are displayed. + +
+ +[//]: # (@@author) ## **Documentation, logging, testing, configuration, dev-ops** @@ -254,7 +710,7 @@ _{Explain here how the data archiving feature will be implemented}_ * [Configuration guide](Configuration.md) * [DevOps guide](DevOps.md) --------------------------------------------------------------------------------------------------------------------- +
## **Appendix: Requirements** @@ -262,77 +718,799 @@ _{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 +* Dog cafe owners who need to manage a team of staff, F&B vendors, and a dog maintenance team. +* Users who prefer typing to other forms of input and who are comfortable using CLI applications. -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +**Value proposition**: PoochPlanner is a desktop application to track details of various groups (`Person`, `Supplier`, `Maintainer`, `Staff`) that dog cafe owners have to regularly interact with. The app is optimized for use using a Command Line Interface (CLI) while still encompassing a user-friendly Graphical User Interface (GUI). +
### 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…​ | +|----------|------------------------|-----------------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| `* * *` | well connected user | add new contacts to my contacts list | have the contacts of new acquaintances in my contacts list | +| `* * *` | cafe owner user | edit my contacts in my contacts list | update contact information such as the new phone number of my contacts | +| `* * *` | cafe owner user | delete contacts | remove outdated contacts such as retrenched staff | +| `* * *` | well connected user | search through my long list of contacts by different specified fields | find my contacts efficiently | +| `* * *` | first-time user | get help about what commands to use | easily know how to navigate the system | +| `* *` | profit-maximising user | sort vendors in ascending order of price | view the vendors selling the cheapest products easily | +| `* *` | careless user | undo my commands | revert my accidental commands easily | +| `* *` | careless user | redo my commands | revert my accidental undo commands easily | +| `* *` | well connected user | pin my contacts in my contacts list | easily view important contacts | +| `* *` | well connected user | unpin my contacts in my contacts list | remove my less important contacts from the top of my list | +| `* *` | profit-maximising user | rate the efficiency of contacts | view the efficiency of my contacts easily and only conduct business with efficient contacts | +| `* *` | forgetful user | note down all details about my contacts | track and remember important details and deadlines easily | +| `* *` | forgetful user | be reminded of my deadlines | complete all my tasks on time | + +
### Use cases -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +[//]: # (@@author yleeyilin) +**System**: `PoochPlanner` + +**Use case**: `UC01 - Adding a contact` + +**Actor**: `User` -**Use case: Delete a person** +**Guarantee**: `If MSS reaches step 3, a new contact is added into the contacts list.` -**MSS** +**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 add the contact of a contact. +2. PoochPlanner updates the contacts list. +3. PoochPlanner confirms the successful addition. Use case ends. -**Extensions** +**Extensions**: + +* 1a. PoochPlanner detects a missing field in the entered input. + * 1a1. PoochPlanner displays the error message. + * 1a2. User re-enters a new command with the required field. + * Steps 1a1 - 1a2 are repeated until the input entered is correct. + * Use case resumes from step 2. + +* 1b. PoochPlanner detects a duplicate name entry. + * 1b1. PoochPlanner displays the error message. + * 1b2. User re-enters a new command with another name. + * Steps 1b1 - 1b2 are repeated until there are no duplicate entries in the input. + * Use case resumes from step 2. + +* 1c. PoochPlanner detects an invalid address format. + * 1c1. PoochPlanner displays the error message. + * 1c2. User re-enters a new command with a correct address format. + * Steps 1c1 - 1c2 are repeated until there is no error with the input. + * Use case resumes from step 2. + +* 1d. PoochPlanner detects an invalid email format. + * 1d1. PoochPlanner displays the error message. + * 1d2. User re-enters a new command with a correct email format. + * Steps 1d1 - 1d2 are repeated until there is no error with the input. + * Use case resumes from step 2. + +* 1e. PoochPlanner detects an invalid input for employment. + * 1e1. PoochPlanner displays the error message. + * 1e2. User re-enters a new command with correct input for employment. + * Steps 1e1 - 1e2 are repeated until there is no error with the input. + * Use case resumes from step 2. + +* 1f. PoochPlanner detects an invalid phone format. + * 1f1. PoochPlanner displays the error message. + * 1f2. User re-enters a new command with a correct phone format. + * Steps 1f1 - 1f2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1g. PoochPlanner detects an invalid salary format. + * 1g1. PoochPlanner displays the error message. + * 1g2. User re-enters a new command with a correct price format. + * Steps 1g1 - 1g2 are repeated until there are no errors in input. + * Use case resumes from step 2. + +* 1h. PoochPlanner detects an invalid price format. + * 1h1. PoochPlanner displays the error message. + * 1h2. User re-enters a new command with a correct price format. + * Steps 1h1 - 1h2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1i. PoochPlanner detects an invalid note format. + * 1i1. PoochPlanner displays the error message. + * 1i2. User re-enters a new command with a correct note format. + * Steps 1i1 - 1i2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1j. PoochPlanner detects an invalid rating format. + * 1j1. PoochPlanner displays the error message. + * 1j2. User re-enters a new command with a correct rating format. + * Steps 1j1 - 1j2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1k. PoochPlanner detects an invalid product format. + * 1k1. PoochPlanner displays the error message. + * 1k2. User re-enters a new command with a correct product format. + * Steps 1k1 - 1k2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1l. PoochPlanner detects an invalid commission format. + * 1l1. PoochPlanner displays the error message. + * 1l2. User re-enters a new command with a correct commission format. + * Steps 1l1 - 1l2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1m. PoochPlanner detects an invalid skill format. + * 1m1. PoochPlanner displays the error message. + * 1m2. User re-enters a new command with a correct skill format. + * Steps 1m1 - 1m2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1n. PoochPlanner detects an invalid name format. + * 1n1. PoochPlanner displays the error message. + * 1n2. User re-enters a new command with a correct name format. + * Steps 1n1 - 1n2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +[//]: # (@@author) + +[//]: # (@@author yleeyilin) + +
+ +**System**: `PoochPlanner` + +**Use case**: `UC02 - Editing a contact` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 3, the contact is successfully edited in the contacts list.` + +**MSS**: + +1. User requests to edit the field of a contact. +2. PoochPlanner updates the field of specified contact. +3. PoochPlanner confirms the successful edit. -* 2a. The list is empty. + Use case ends. + +**Extensions**: + +* 1a. PoochPlanner detects a missing name field in the entered input. + * 1a1. PoochPlanner displays the error message. + * 1a2. User re-enters a new command with the name field. + * Steps 1a1 - 1a2 are repeated until the input entered is correct. + * Use case resumes from step 2. + +* 1b. PoochPlanner is unable to find the contact. + * 1b1. PoochPlanner displays the error message. + * 1b2. User re-enters a new command with another name. + * Steps 1b1 - 1b2 are repeated until the input references a contact that exists in PoochPlanner. + * Use case resumes from step 2. + +* 1c. PoochPlanner detects an unknown input for employment. + * 1c1. PoochPlanner displays the error message. + * 1c2. User re-enters a new command with correct input for employment. + * Steps 1c1 - 1c2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1d. PoochPlanner detects empty field in the entered input. + * 1d1. PoochPlanner displays the error message. + * 1d2. User re-enters a new command and specifies the field(s) to edit. + * Steps 1d1 - 1d2 are repeated until a valid field is specified. + * Use case resumes from step 2. + +* 1e. User specifies an invalid field. + * 1e1. PoochPlanner displays the error message. + * 1e2. User re-enters a new command with a correct field format. + * Steps 1e1 - 1e2 are repeated until a valid field is specified. + * Use case resumes from step 2. + +* 1f. PoochPlanner detects an invalid email format. + * 1f1. PoochPlanner displays the error message. + * 1f2. User re-enters a new command with a correct email format. + * Steps 1f1 - 1f2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1g. PoochPlanner detects an invalid phone format. + * 1g1. PoochPlanner displays the error message. + * 1g2. User re-enters a new command with a correct phone format. + * Steps 1g1 - 1g2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1h. PoochPlanner detects an invalid salary format. + * 1h1. PoochPlanner displays the error message. + * 1h2. User re-enters a new command with a correct salary format. + * Steps 1h1 - 1h2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1i. PoochPlanner detects an invalid price format. + * 1i1. PoochPlanner displays the error message. + * 1i2. User re-enters a new command with a correct price format. + * Steps 1i1 - 1i2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1j. PoochPlanner detects an invalid address format. + * 1j1. PoochPlanner displays the error message. + * 1j2. User re-enters a new command with a correct address format. + * Steps 1j1 - 1j2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1k. PoochPlanner detects an invalid commission format. + * 1k1. PoochPlanner displays the error message. + * 1k2. User re-enters a new command with a correct commission format. + * Steps 1k1 - 1k2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1l. PoochPlanner detects an invalid product format. + * 1l1. PoochPlanner displays the error message. + * 1l2. User re-enters a new command with a correct product format. + * Steps 1l1 - 1l2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1m. PoochPlanner detects an invalid name format. + * 1m1. PoochPlanner displays the error message. + * 1m2. User re-enters a new command with a correct name format. + * Steps 1m1 - 1m2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +* 1n. PoochPlanner detects an invalid skill format. + * 1n1. PoochPlanner displays the error message. + * 1n2. User re-enters a new command with a correct skill format. + * Steps 1n1 - 1n2 are repeated until there are no errors with the input. + * Use case resumes from step 2. + +
+ +[//]: # (@@author) + +[//]: # (@@author yleeyilin) + +**System**: `PoochPlanner` + +**Use case**: `UC03 - Searching for a contact` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 3, the contacts list is filtered successfully.` + +**MSS**: + +1. User requests to search for the contact of a person with a keyword for a specified field. +2. PoochPlanner confirms successful search. +3. PoochPlanner returns the filtered list of contacts that matches the keyword as specified by the user. - Use case ends. + Use case ends. -* 3a. The given index is invalid. +**Extensions**: - * 3a1. AddressBook shows an error message. +* 1a. PoochPlanner detects a missing field in the entered input. + * 1a1. PoochPlanner displays the error message. + * 1a2. User re-enters a new command with a specified field. + * Steps 1a1 - 1a2 are repeated until a valid field is entered by the user. + * Use case resumes from step 2. - Use case resumes at step 2. +* 1b. PoochPlanner detects duplicate fields in the entered input. + * 1b1. PoochPlanner displays the error message. + * 1b2. User re-enters a new command with a specified field. + * Steps 1b1 - 1b2 are repeated until the command does not contain any duplicate fields. + * Use case resumes from step 2. -*{More to be added}* +
-### Non-Functional Requirements +[//]: # (@@author) + +[//]: # (@@author yleeyilin) + +**System**: `PoochPlanner` + +**Use case**: `UC04 - Deleting a contact` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 3, the contact is deleted from the contacts list.` + +**MSS**: + +1. User requests to delete a contact. +2. PoochPlanner removes the contact and updates the contacts list. +3. PoochPlanner confirms the successful deletion. + + Use case ends. + +**Extensions**: + +* 1a. PoochPlanner detects a missing name field in the entered input. + * 1a1. PoochPlanner displays the error message. + * 1a2. User re-enters a new command with the name field. + * Steps 1a1 - 1a2 are repeated until the input entered is correct. + * Use case resumes from step 2. + +* 1b. PoochPlanner is unable to find the contact. + * 1b1. PoochPlanner displays the error message. + * 1b2. User re-enters a new command with another name. + * Steps 1b1 - 1b2 are repeated until the input name matches a contact name that exists in PoochPlanner. + * Use case resumes from step 2. + +
+ +[//]: # (@@author) + +**System**: `PoochPlanner` + +**Use case**: `UC05 - Rating a contact` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 3, a rating for the contact is updated successfully in the contacts list.` + +**MSS**: + +1. User requests to rate a contact with the specified rating. +2. PoochPlanner updates the contact rating with the rating provided. +3. PoochPlanner confirms the successful rating of the contact. + + Use case ends. + +**Extensions**: + +* 1a. PoochPlanner detects a missing name in the entered input. + * 1a1. PoochPlanner displays the error message. + * 1a2. User re-enters a new command with a specified name. + * Steps 1a1 - 1a2 are repeated until a valid name is input by the User. + * Use case resumes from step 2. + +* 1b. PoochPlanner detects an invalid name in the entered input. + * 1b1. PoochPlanner displays the error message. + * 1b2. User re-enters a new command with a specified name. + * Steps 1b1 - 1b2 are repeated until a valid name is input by the User. + * Use case resumes from step 2. + +* 1c. PoochPlanner detects a missing rating in the entered input. + * 1c1. PoochPlanner displays the error message. + * 1c2. User re-enters a new command with a new rating value. + * Steps 1c1 - 1c2 are repeated until the rating provided is an integer between 0 and 5 inclusive. + * Use case resumes from step 2. + +* 1d. PoochPlanner detects an invalid rating in the entered input. + * 1d1. PoochPlanner displays the error message. + * 1d2. User re-enters a new command with a new rating value. + * Steps 1d1 - 1d2 are repeated until the rating provided is an integer between 0 and 5 inclusive. + * Use case resumes from step 2. + +
+ +[//]: # (@@author yleeyilin) + +**System**: `PoochPlanner` + +**Use case**: `UC06 - Pinning a contact` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 3, the user has successfully pinned the contact.` + +**MSS**: + +1. User requests to pin a contact. +2. The specified contact is pinned successfully. +3. PoochPlanner displayed the contacts list with the pinned contacts at the top. + + Use case ends. + +* 1a. PoochPlanner detects a missing name field in the entered input. + * 1a1. PoochPlanner displays the error message. + * 1a2. User re-enters a new command with a specified name field. + * Steps 1a1 - 1a2 are repeated until the input entered is correct. + * Use case resumes from step 2. + +* 1b. PoochPlanner detects an invalid name field in the entered input. + * 1b1. PoochPlanner displays the error message. + * 1a2. User re-enters a new command with a specified name field. + * Steps 1b1 - 1b2 are repeated until the input entered is correct. + * Use case resumes from step 2. + +* 1c. PoochPlanner fails to find the person. + * 1c1. PoochPlanner displays the error message. + * 1c2. User re-enters a new command with another name. + * Steps 1c1 - 1c2 are repeated until the input name matches a contact name that exists in PoochPlanner. + * Use case resumes from step 2. + +
+ +[//]: # (@@author) + +[//]: # (@@author yleeyilin) + +**System**: `PoochPlanner` + +**Use case**: `UC07 - Unpinning a contact` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 3, the user has successfully unpinned the contact.` + +**MSS**: + +1. User requests to unpin a contact. +2. The specified contact is unpinned successfully. +3. PoochPlanner updates the contacts list with the remaining pinned contacts at the top. + + Use case ends. -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. +**Extensions**: -*{More to be added}* +* 1a. PoochPlanner detects a missing name field in the entered input. + * 1a1. PoochPlanner displays the error message. + * 1a2. User re-enters a new command with a specified name field. + * Steps 1a1 - 1a2 are repeated until the input entered is correct. + * Use case resumes from step 2. + +* 1b. PoochPlanner detects an invalid name field in the entered input. + * 1b1. PoochPlanner displays the error message. + * 1b2. User re-enters a new command with a specified name field. + * Steps 1b1 - 1b2 are repeated until the input entered is correct. + * Use case resumes from step 2. + +* 1c. PoochPlanner fails to find the person. + * 1c1. PoochPlanner displays the error message. + * 1c2. User re-enters a new command with another name. + * Steps 1c1 - 1c2 are repeated until the input name matches a contact name that exists in PoochPlanner. + * Use case resumes from step 2. + +
+ +[//]: # (@@author) + +**System**: `PoochPlanner` + +**Use case**: `UC08 - Sorting the contacts list` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 3, the user has successfully sorted the contacts list by a specified field.` + +**MSS**: + +1. User requests to sort PoochPlanner by a specified field. +2. PoochPlanner updates the contacts list in the sorted order. +3. PoochPlanner confirms that the contacts list has been successfully sorted. + + Use case ends. + +**Extensions**: + +* 1a. PoochPlanner detects a missing field in the entered input. + * 1a1. PoochPlanner displays the error message. + * 1a2. User re-enters a new command with a specified name. + * Steps 1a1 - 1a2 are repeated until a valid name is input by the User. + * Use case resumes from step 2. + +* 1b. PoochPlanner detects an invalid field in the entered input. + * 1b1. PoochPlanner displays the error message. + * 1b2. User re-enters a new command with a specified name. + * Steps 1b1 - 1b2 are repeated until a valid field is input by the User. + * Use case resumes from step 2. + +
+ +[//]: # (@@author jannaleong) + +**System**: `PoochPlanner` + +**Use case**: `UC09 - Adding a note to a contact` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 3, a note for the contact specified is updated successfully in the contacts list.` + +**MSS**: + +1. User requests to add a note to the contact. +2. PoochPlanner updates the contact with the specified note. +3. PoochPlanner confirms that the note has been successfully added. + + Use case ends. + +**Extensions**: + +* 1a. PoochPlanner detects a missing name in the entered input. + * 1a1. PoochPlanner displays the error message. + * 1a2. User re-enters a new command with name value. + * Steps 1a1 - 1a2 are repeated until a valid name value is input by the user. + * Use case resumes from step 2. + +* 1b. PoochPlanner detects an invalid name in the entered input. + * 1b1. PoochPlanner displays the error message. + * 1b2. User re-enters a new command with a new name value. + * Steps 1b1 - 1b2 are repeated until a valid name value is input by the user. + * Use case resumes from step 2. + +* 1c. PoochPlanner detects a missing note in the entered input. + * 1c1. PoochPlanner displays the error message. + * 1c2. User re-enters a new command with a note value. + * Steps 1c1 - 1c2 are repeated until the note value is provided (non-null/non-empty). + * Use case resumes from step 2. + +* 1d. PoochPlanner detects an invalid note in the entered input. + * 1d1. PoochPlanner displays the error message. + * 1d2. User re-enters a new command with a new note value. + * Steps 1d1 - 1d2 are repeated until the note provided is valid (non-null/non-empty). + * Use case resumes from step 2. + +
+ +**System**: `PoochPlanner` + +**Use case**: `UC10 - Adding a deadline note to a contact` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 3, a note with a deadline for the specified contact will be updated successfully in the contacts list.` + +**MSS**: + +1. User requests to add a deadline note to the contact. +2. PoochPlanner updates the contact with the specified deadline note. +3. PoochPlanner confirms that the deadline note has been successfully added. + + Use case ends. + +**Extensions**: + +* 1a. PoochPlanner detects a missing name in the entered input. + * 1a1. PoochPlanner displays the error message. + * 1a2. User re-enters a new command with a name value. + * Steps 1a1 - 1a2 are repeated until a name is input by the user. + * Use case resumes from step 2. + +* 1b. PoochPlanner detects an invalid name in the entered input. + * 1b1. PoochPlanner displays the error message. + * 1b2. User re-enters a new command with a new name value. + * Steps 1b1 - 1b2 are repeated until a valid name is input by the user. + * Use case resumes from step 2. + +* 1c. PoochPlanner detects a missing note in the entered input. + * 1c1. PoochPlanner displays the error message. + * 1c2. User re-enters a new command with a note value. + * Steps 1c1 - 1c2 are repeated until the a note value is provided (non-null/non-empty). + * Use case resumes from step 2. + +* 1d. PoochPlanner detects an invalid note in the entered input. + * 1d1. PoochPlanner displays the error message. + * 1d2. User re-enters a new command with a new note value. + * Steps 1d1 - 1d2 are repeated until the note provided is valid (non-null/non-empty). + * Use case resumes from step 2. + +* 1e. PoochPlanner detects an invalid deadline in the entered input. + * 1e1. PoochPlanner displays the error message. + * 1e2. User re-enters a new command with a new deadline value. + * Steps 1e1 - 1e2 are repeated until the deadline provided is valid (non-null/non-empty). + * Use case resumes from step 2. + +
+ +[//]: # (@@author) + +[//]: # (@@author chiageng) + +**System**: `PoochPlanner` + +**Use case**: `UC11 - Undoing a command` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 2, the user has successfully reverted back to the previous command.` + +**MSS**: + +1. User requests to undo a previous command. +2. PoochPlanner retrieves a previous record of the address book data. + + Use case ends. + +**Extensions**: + +* 1a. PoochPlanner detects no previous record of the address book data. + * 1a1. PoochPlanner displays the error message. + * Use case ends. + +[//]: # (@@author) + +
+ +[//]: # (@@author chiageng) + +**System**: `PoochPlanner` + +**Use case**: `UC12 - Redoing a command` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 2, the user has successfully reverted back the undo command.` + +**MSS**: + +1. User requests to redo a previous command. +2. PoochPlanner retrieves a future record of the address book data. + + Use case ends. + +**Extensions**: + +* 1a. PoochPlanner detects no future record of the address book data. + * 1a1. PoochPlanner displays the error message. + * Use case ends. + +[//]: # (@@author) + +
+ +[//]: # (@@author jannaleong) + +**System**: `PoochPlanner` + +**Use case**: `UC13 - Viewing help` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 2, the help window for the corresponding command pops up.` + +**MSS**: + +1. User requests to get help about a command. +2. PoochPlanner displays help details relating to this command. + + Use case ends. + +**Extensions**: + +* 1a. User requests help for an invalid command (a command that is not offered by PoochPlanner). + * 1a1. PoochPlanner displays the error message. + * 1a2. User re-enters a new command and request to learn about a new command. + * Steps 1a1 - 1a2 are repeated until a valid command is entered by the user. + * Use case resumes from step 2. + +
+ +**System**: `PoochPlanner` + +**Use case**: `UC14 - Viewing reminders for the contacts list` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 2, contacts will be displayed only if their note deadlines are on or after today's date.` + +**MSS**: + +1. User requests to receive reminders. +2. PoochPlanner displays all relevant contacts. + + Use case ends. + +[//]: # (@@author) + +**System**: `PoochPlanner` + +**Use case**: `UC15 - Clearing the contacts list` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 3, the user has successfully cleared the contacts list.` + +**MSS**: + +1. User requests to clear the data in the contacts list. +2. PoochPlanner updates the data in the contacts list. +3. PoochPlanner confirms that the data in the contacts list has been cleared. + + Use case ends. + +
+ +**System**: `PoochPlanner` + +**Use case**: `UC16 - Listing all contacts` + +**Actor**: `User` + +**Guarantee**: `If MSS reaches step 3, the user has successfully listed all the contacts.` + +**MSS**: + +1. User requests to list all contacts. +2. PoochPlanner displays all relevant contacts. +3. PoochPlanner confirms that all relevant contacts has been successfully listed. + + Use case ends. + +
+ + +### Non-Functional Requirements + +1. PoochPlanner needs to be compatible across major operating systems, including Windows, macOS, and Linux, supporting only Java 11. +2. User-managed transactions and budgets should be locally saved and backed up, ensuring restoration in subsequent sessions unless data integrity is compromised. +3. Thorough documentation of all non-private methods is essential to ensure the maintainability of the codebase. +4. PoochPlanner should function completely offline. +5. PoochPlanner should be able to hold up to 1000 contacts without a noticeable sluggishness in performance for typical usage. +6. A user with above average typing speed should be able to accomplish most of the tasks faster using commands than using the mouse. +7. All code snippets presented in the developer guide shall follow a consistent coding style and formatting, adhering to the module's coding standards and best practices. +8. The developer guide shall undergo regular content audits, with outdated or deprecated information flagged for removal or revision, and new features or updates documented within one week of release. +9. The system should respond within 2 seconds. +10. The data should be stored locally and should not be accessible from other devices due to privacy issues. + +
### Glossary -* **Mainstream OS**: Windows, Linux, Unix, MacOS -* **Private contact detail**: A contact detail that is not meant to be shared with others +* **PoochPlanner**: An address book CLI software that stores contacts. +* **Contact**: A contact that is stored in PoochPlanner. +* **Supplier**: External suppliers that sell the logistics required for the sustenance of dog cafe operations, for example dog food, to the dog cafe owners at a fixed price. +* **Staff**: Employees of the dog cafe that handle the running of the cafe. +* **Maintainer**: Specialized external workers that take special care of and maintain the dogs. +* **CLI**: Command Line Interface +* **GUI**: Graphical User Interface +* **MSS**: Main Success Scenario +* **JSON**: JavaScript Object Notation +* **API**: Application Programming Interface --------------------------------------------------------------------------------------------------------------------- +
+ +## **Appendix: Planned Enhancements** + +1. Enhance commands to be space-insensitive + 1. Currently, we do not allow for incorrect spacings in commands. + 2. `/add-person ; name : Person1 ;phone :98883888;address:Pooch Street 32 ; email : impooch@gmail.com`. + 3. The above example will be considered as invalid since there is no spacing before the `phone` prefix and before the `address` prefix. The lack of spacing causes `phone` and `address` to not be parsed as valid prefixes. + 4. We plan to extend PoochPlanner to accept alternative possible inputs with varied spacings to cater fast typists as varied spacings are likely to occur due to typing errors + +2. Enhance commands to fix multiple white spacings in the user's input + 1. Currently, we do not have any checker to verify if there are multiple white spacings in the user's input. + 2. We take any input values `John Doe` with multiple number of spacings as different inputs. + 3. We plan to parse all inputs to remove additional spacings to cater fast typists as additional spacings are likely to occur due to typing errors. + +3. Enhance prices to allow for decimal places + 1. Currently, we do not allow prices to have decimal places. + 2. We plan to allow decimal places for prices to allow for greater flexibility in recording prices. + +4. Enhance salaries to allow for storage in different units + 1. Currently, we only allow storing hourly salaries with the unit `/hr`. + 2. We plan to allow for more flexible units such as `/day`, `/month` and `/event`. + +5. Enhance validation on input fields for search command + 1. Currently, we do not have any validation on input fields such as salary and phone in search commands. + 2. If users insert a random word in the salary field, the execution will not throw any error. + 3. We plan to do validation checks on all fields to ensure that users are inserting the correct type of value in the field. + +6. Enhance post-search status + 1. Currently, after a search command, the contact book will only display the filtered contacts list. + 2. Execution of delete, pin, unpin, undo and redo will not return to the full contacts list. + 3. We plan to enhance the commands by returning to the full list after every command execution. + +7. Enhance commissions to allow for decimal places + 1. Currently, we do not allow commissions to have decimal places. + 2. We plan to allow decimal places for commissions to allow for greater flexibility in recording commissions. + +8. Enhance commissions to allow for storage in different units + 1. Currently, we only allow storing hourly commissions with the unit `/hr`. + 2. We plan to allow for more flexible units such as `/day`, `/month` and `/event`. + +9. Enhance phone number storage + 1. Currently, we only allow users to add one phone number to one contact. + 2. We plan to allow users to add more than one phone number to allow for greater flexibility in storing contacts. + +10. Enhance undo command upon pinning or unpinning + 1. Currently, when using pin command two or more times, calling undo once will not revert the pin operation. This is similar for unpin since they both share the same implementation. + 2. We plan to allow users to use undo only once to undo all repeated and consecutive pin/unpin attempts. + +
## **Appendix: Instructions for manual testing** -Given below are instructions to test the app manually. +Below are instructions to test the app manually. Before each test, run `/clear` to reset the data in PoochPlanner. +Also, take caution when copying the commands to the input box as our commands are space sensitive. Line breaks may result +in spaces being omitted.
:information_source: **Note:** These instructions only provide a starting point for testers to work on; testers are expected to do more *exploratory* testing. @@ -343,40 +1521,365 @@ 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 `[CS2103T-W10-2][PoochPlanner].jar` file and copy it 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. + 2. Double-click the `[CS2103T-W10-2][PoochPlanner].jar` file.
+ Expected: Shows the GUI with an empty contacts list. -1. Saving window preferences +2. Saving window preferences 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.
+ 2. Re-launch the app by double-clicking the `[CS2103T-W10-2][PoochPlanner].jar` file.
Expected: The most recent window size and location is retained. -1. _{ more test cases …​ }_ +
+ +[//]: # (@@author chiageng) + +### Adding a contact + +1. Adding a `Person` contact + + 1. Prerequisites: The specified name of the contact must not already exist in the contacts list. + + 2. Test case: `/add-person ; name : Person1 ; phone : 98883888 ; address : Pooch Street 32 ; email : impooch@gmail.com`
+ Expected: Adds a person named "Person1" into the contacts list. Details of the added contact is shown in the status message. + +2. Adding a `Staff` contact + + 1. Prerequisites: The specified name of the contact must not already exist in the contacts list. + + 2. Test case: `/add-staff ; name : Staff1 ; phone : 98765435 ; address : Poochie Street 21 ; email : ilovecatstoo@gmail.com ; salary : $50/hr ; employment : part-time`
+ Expected: Adds a staff named "Staff1" into the contacts list. Details of the added contact is shown in the status message. + +3. Adding a `Supplier` contact + + 1. Prerequisites: The specified name of the contact must not already exist in the contacts list. + + 2. Test case: `/add-supplier ; name : Supplier1 ; phone : 98673098 ; address : Meow Street 24 ; email : ilovewombatstoo@gmail.com ; product : kibble ; price : $98/bag`
+ Expected: Adds a supplier named "Supplier1" into the contacts list. Details of the added contact is shown in the status message. + +4. Adding a `Maintainer` contact + + 1. Prerequisites: The specified name of the contact must not already exist in the contacts list. + + 2. Test case: ` /add-maintainer ; name : Maintainer1 ; phone : 98765435 ; address : Poochie Street 24 ; email : ihelppooches@gmail.com ; skill : trainer ; commission : $60/hr`
+ Expected: Adds a maintainer named "Maintainer1" into the contacts list. Details of the added contact is shown in the status message. + +[//]: # (@@author yleeyilin) + +
+ +### Editing a contact + +1. Editing a `Person` contact + + 1. Prerequisites: The contact to be edited must already exist and should have been added as a `Person` type. You can run the following command to add in a `Person` to edit:
+ `/add-person ; name : Person1 ; phone : 98883888 ; address : Pooch Street 32 ; email : impooch@gmail.com` + + 2. Test case: `/edit-person ; name : Person1 ; field : { phone : 99820520 }`
+ Expected: The phone field of contact named "Person1" is edited to `99820520`. Details of the edited contact is shown in the status message. + + 3. Test case: `/edit-person ; name : Person1 ; field : { address : Pooch Street 31 }`
+ Expected: The address field of contact named "Person1" is edited to `Pooch Street 31`. Details of the edited contact is shown in the status message. + + 4. Test case: `/edit-person ; name : Person1 ; field : { phone : 99990520 ; email : impooch@gmail13.com }`
+ Expected: The phone and email field of contact named "Person1" is edited to `99990520` and `impooch@gmail13.com` respectively. Details of the edited contact is shown in the status message. + +2. Editing a `Staff` contact + + 1. Prerequisites: The contact to be edited must already exist and should have been added as a `Staff` type. You can run the following command to add in a `Staff` to edit:
+ `/add-staff ; name : Staff1 ; phone : 98765435 ; address : Poochie Street 21 ; email : ilovecatstoo@gmail.com ; salary : $50/hr ; employment : part-time` + + 2. Test case: `/edit-staff ; name : Staff1 ; field : { phone : 99820520 }`
+ Expected: The phone field of contact named "Staff1" is edited to `99820520`. Details of the edited contact is shown in the status message. + + 3. Test case: `/edit-staff ; name : Staff1 ; field : { salary : $55/hr }`
+ Expected: The salary field of contact named "Staff1" is edited to `$55/hr`. Details of the edited contact is shown in the status message. + + 4. Test case: `/edit-staff ; name : Staff1 ; field : { employment : full-time }`
+ Expected: The employment field of contact named "Staff1" is edited to `full-time`. Details of the edited contact is shown in the status message. + + 5. Test case: `/edit-staff ; name : Staff1 ; field : { salary : $40/hr ; employment : part-time }`
+ Expected: The salary and employment field of contact named "Staff1" is edited to `40/hr` and `part-time` respectively. Details of the edited contact is shown in the status message. + +3. Editing a `Supplier` contact + + 1. Prerequisites: The contact to be edited must already exist and should have been added as a `Supplier` type. You can run the following command to add in a `Supplier` to edit:
+ `/add-supplier ; name : Supplier1 ; phone : 98673098 ; address : Meow Street 24 ; email : ilovewombatstoo@gmail.com ; product : kibble ; price : $98/bag` + + 2. Test case: `/edit-supplier ; name : Supplier1 ; field : { phone : 9994555 }`
+ Expected: The phone field of contact named "Supplier1" is edited to `9994555`. Details of the edited contact is shown in the status message. + + 3. Test case: `/edit-supplier ; name : Supplier1 ; field : { product : dogdiapers }`
+ Expected: The product field of contact named "Supplier1" is edited to `dogdiapers`. Details of the edited contact is shown in the status message. + + 4. Test case: `/edit-supplier ; name : Supplier1 ; field : { price : $10/bag }`
+ Expected: The price field of contact named "Supplier1" is edited to `$10/bag`. Details of the edited contact is shown in the status message. + + 5. Test case: `/edit-supplier ; name : Supplier1 ; field : { product : kibbles ; price : $75/bag }`
+ Expected: The product and price field of contact named "Supplier1" is edited to `kibbles` and `$75/bag` respectively. Details of the edited contact is shown in the status message. + +4. Editing a `Maintainer` contact + + 1. Prerequisites: The contact to be edited must already exist and should have been added as a `Maintainer` type. You can run the following command to add in a `Maintainer` to edit:
+ `/add-maintainer ; name : Maintainer1 ; phone : 98765435 ; address : Poochie Street 24 ; email : ihelppooches@gmail.com ; skill : trainer ; commission : $60/hr` + + 2. Test case: `/edit-maintainer ; name : Maintainer1 ; field : { phone : 84444555 }`
+ Expected: The phone field of contact named "Maintainer1" is edited to `84444555`. Details of the edited contact is shown in the status message. + + 3. Test case: `/edit-maintainer ; name : Maintainer1 ; field : { commission : $10/hr }`
+ Expected: The commission field of contact named "Maintainer1" is edited to `$10/hr`. Details of the edited contact is shown in the status message. + + 4. Test case: `/edit-maintainer ; name : Maintainer1 ; field : { skill : cleaner }`
+ Expected: The skill field of contact named "Maintainer1" is edited to `cleaner`. Details of the edited contact is shown in the status message. + + 5. Test case: `/edit-maintainer ; name : Maintainer1 ; field : { commission : $12/hr ; skill : janitor }`
+ Expected: The commission and skill field of contact named "Maintainer1" is edited to `$12/hr` and `janitor` respectively. Details of the edited contact is shown in the status message. + +
+ +[//]: # (@@author) + +### Searching a contact + +1. Searching contacts by name + + 1. Prerequisites: The contact list must already have some contacts for testing purposes. You may run the following commands to help in testing:
+ `/add-person ; name : Poochie ; phone : 12345678 ; address : Pooch Street 32 ; email : impoochie@gmail.com`
+ `/add-person ; name : John Doe ; phone : 88888888 ; address : Pooch Street 32 ; email : imjohndoe@gmail.com`
+ `/add-person ; name : John ; phone : 23452345 ; address : Pooch Street 32 ; email : imjohn@gmail.com` -### Deleting a person + 2. Test case: `/search ; name : John`
+ Expected: Displays contacts with the names "John" and "John Doe". -1. Deleting a person while all persons are being shown +2. Searching contacts by phone number - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. + 1. Prerequisites: The contact list must already have some contacts for testing purposes. You may run the following commands to help in testing:
+ `/add-person ; name : Poochie ; phone : 12345678 ; address : Pooch Street 32 ; email : impoochie@gmail.com`
+ `/add-person ; name : John Doe ; phone : 8888888 ; address : Pooch Street 32 ; email : imjohndoe@gmail.com`
+ `/add-person ; name : John ; phone : 23452345 ; address : Pooch Street 32 ; email : imjohn@gmail.com`
- 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. + 2. Test case: `/search ; phone : 12345678`
+ Expected: Displays only one contact named "Poochie" with the phone number `12345678`. - 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. +
- 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
+### Deleting a contact + +1. Deleting a contact while all contacts are being shown + + 1. Prerequisites: Only **one** contact with the name **_Poochie_** should exist in PoochPlanner. If not, run the following command to ensure add **_Poochie_** into PoochPlanner. PoochPlanner does not accept duplicate names so there will not be an instance where there is more than one contact with the name **_Poochie_** that exists in the contacts list.
+ `/add-person ; name : Poochie ; phone : 98883888 ; address : Pooch Street 32 ; email : impoochie@gmail.com` + + 2. Test case: `/delete ; name : Poochie`
+ Expected: Contact named **_Poochie_** is deleted from the list. Contact type and name of the deleted contact is shown in the status message. Timestamp in the status bar is updated. + + 3. Test case: `/delete ; name : Moochie`
+ Expected: No contact is deleted. Error details shown in the status message. Status bar remains the same. + + 4. Test case: `/delete`
+ Expected: No contact is deleted. Error details shown in the status message. Status bar remains the same. + + 5. Other incorrect delete commands to try: `/delete`, `delete ; name :`
Expected: Similar to previous. -1. _{ more test cases …​ }_ +
+ +### Rating a contact + +1. Rating a contact while all contacts are being shown + + 1. Prerequisites: Only **one** contact with the name **_Poochie_** should exist in PoochPlanner. If not, run the following command to ensure add **_Poochie_** into PoochPlanner. PoochPlanner does not accept duplicate names so there will not be an instance where there is more than one contact with the name **_Poochie_** that exists in the contacts list.
+ `/add-person ; name : Poochie ; phone : 98883888 ; address : Pooch Street 32 ; email : impoochie@gmail.com` + + 2. Test case: `/rate ; name : Poochie ; rating : 5`
+ Expected: Contact named **_Poochie_** is updated with a rating of 5. Contact type and name of the rated contact is shown in the status message. Timestamp in the status bar is updated. + + 3. Test case: `/rate ; name : Moochie ; rating : 5`
+ Expected: No contact is rated. Error details shown in the status message. Status bar remains the same. + + 4. Test case: `/rate ; name : Poochie ; rating : 6`
+ Expected: No contact is rated. Error details shown in the status message. Status bar remains the same. + +
+ +[//]: # (@@author yleeyilin) + +### Pinning a contact + +1. Pinning a contact while all contacts are being shown + + 1. Prerequisites: Only **one** contact with the name **_Poochie_** should exist in PoochPlanner. If not, run the following command to ensure add **_Poochie_** into PoochPlanner. PoochPlanner does not accept duplicate names so there will not be an instance where there is more than one contact with the name **_Poochie_** that exists in the contacts list.
+ `/add-person ; name : Poochie ; phone : 98883888 ; address : Pooch Street 32 ; email : impoochie@gmail.com` + + 2. Test case: `/pin ; name : Poochie`
+ Expected: Contact named **_Poochie_** is pinned at the top of the contact list. + +### Unpinning a contact + +1. Unpinning a contact while all contacts are being shown + + 1. Prerequisites: Only **one** contact with the name **_Poochie_** should exist in PoochPlanner. If not, run the following command to ensure add **_Poochie_** into PoochPlanner. PoochPlanner does not accept duplicate names so there will not be an instance where there is more than one contact with the name **_Poochie_** that exists in the contacts list.
+ `/add-person ; name : Poochie ; phone : 98883888 ; address : Pooch Street 32 ; email : impoochie@gmail.com`
+ `/pin ; name : Poochie` + + 2. Test case: `/unpin ; name : Poochie`
+ Expected: Contact named **_Poochie_** is no longer pinned at the top of the contact list. + +
+ +[//]: # (@@author) + +### Sorting contacts list + +1. Sorting contacts by name + + 1. Prerequisites: The contacts list must have some contacts for testing purposes. You may run the following commands first to help in testing:
+ `/add-person ; name : Poochie ; phone : 12345678 ; address : Pooch Street 32 ; email : impoochie@gmail.com`
+ `/add-person ; name : John Doe ; phone : 88888888 ; address : Pooch Street 32 ; email : imjohndoe@gmail.com`
+ `/add-person ; name : John ; phone : 23452345 ; address : Pooch Street 32 ; email : imjohn@gmail.com`
+ + 2. Test case: `/sort ; field : name`
+ Expected: Displays all contacts sorted by name in ascending order. + +2. Sorting contacts by phone number + + 1. Prerequisites: The contacts list must have some contacts for testing purposes. You may run the following commands first to help in testing:
+ `/add-person ; name : Poochie ; phone : 12345678 ; address : Pooch Street 32 ; email : impoochie@gmail.com`
+ `/add-person ; name : John Doe ; phone : 88888888 ; address : Pooch Street 32 ; email : imjohndoe@gmail.com`
+ `/add-person ; name : John ; phone : 23452345 ; address : Pooch Street 32 ; email : imjohn@gmail.com` + + 2. Test case: `/sort ; field : phone`
+ Expected: Displays all contacts sorted by phone number in ascending order. + +
+ +[//]: # (@@author jannaleong) + +### Adding a note to a contact + +1. Adding a note (no deadline) to a contact + + 1. Prerequisites: The contact to add a note to must already exist. This contact can be of `Person`/`Supplier`/`Staff`/`Maintainer` type. You can run the following command to add a contact:
+ `/add-person ; name : Poochie ; phone : 98883888 ; address : Pooch Street 32 ; email : impoochie@gmail.com` + + 2. Test case:
`/note ; name : Poochie ; note : get kibble`
+ Expected: Adds a note to a contact named **_Poochie_**. + +2. Adding a note (with deadline) to a contact + + 1. Prerequisites: The contact to add a note to must already exist. This contact can be of `Person`/`Supplier`/`Staff`/`Maintainer` type. You can run the following command to add a contact:
+ `/add-person ; name : Poochie ; phone : 98883888 ; address : Pooch Street 32 ; email : impoochie@gmail.com` + + 2. Test case: `/note ; name : Poochie ; note : get kibble ; deadline : 2024-10-10`
+ Expected: Adds a note with deadline to a contact named **_Poochie_**. + +
+ +[//]: # (@@author) + +[//]: # (@@author chiageng) + +### Undoing a command + +1. Undoing a command that modifies the contacts list + + 1. Prerequisites: The previous command must have modified the contacts list. You may run the following command first to modify the contact book:
+ `/add-person ; name : Poochie ; phone : 98883888 ; address : Pooch Street 32 ; email : impoochie@gmail.com` + + 2. Test case: `/undo`
+ Expected: Reverts the changes in the contacts list to just before executing the `add-person` command. + +2. Undoing a command that does not modify the contacts list + + 1. Prerequisites: The previous command must not have made any modifications to the contacts list. You may run the following two commands, whereby the second command does not modify the contacts list:
+ `/add-person ; name : Poochie ; phone : 98883888 ; address : Pooch Street 32 ; email : impoochie@gmail.com`
+ `/search ; name : Poochie` + + 2. Test case: `/undo`
+ Expected: In this case, as no modifications were made directly to the contacts list upon performing the `search` command, the `undo` command reverts back the changes to just before the `add-person` is executed. + +[//]: # (@@author) + +
+ +[//]: # (@@author chiageng) + +### Redoing a command +1. Redoing an undo command + + 1. Prerequisites: There must have been at least one undo command executed. You may run the following command before testing:
+ `/add-person ; name : Poochie ; phone : 98883888 ; address : Pooch Street 32 ; email : impoochie@gmail.com`
+ `/undo` + + 2. Test case: `/redo`
+ Expected: Reverts the changes caused by the `undo` command to just right after `add-person` command is executed. + +[//]: # (@@author) + +
+ +[//]: # (@@author jannaleong) + +### Viewing reminders + +1. Viewing a reminder + + 1. Prerequisites: There must be a contact with a note that has a deadline on or after today's date. You may run the following commands to add such a contact:
+ `/add-person ; name : Poochie ; phone : 98883888 ; address : Pooch Street 32 ; email : impoochie@gmail.com`
+ `/note ; name : Poochie ; note : get kibble ; deadline : 2024-10-10` + + 2. Test case: `/remind`
+ Expected: Displays the contact named **_Poochie_** with the note deadline after today (note: if there are other contacts in the contacts list with notes that have deadlines on or after today's date, they will also appear). + +
+ +### Viewing help + +1. Viewing help + + 1. Test case: `/help ; command : delete`
+ Expected: Displays help details for the delete command. + +
+ +[//]: # (@@author) + +[//]: # (@@author chiageng) + +### Appendix : Effort +#### Project Overview +Our project aimed to enhance the functionality of a contact management system, building upon the foundation laid by AB3 (Address Book 3). Key improvements included accommodating multiple types of contacts, refining command formats for user-friendliness, introducing dynamic search and sorting capabilities, implementing note and reminder features, integrating pin and unpin functionalities, and incorporating undo and redo functionalities. These enhancements aimed to provide users with a more intuitive and efficient contact management experience. + +#### Difficulty Level and Challenges Faced +The project faced significant challenges due to its complexity and the need to seamlessly integrate new features with the existing AB3 framework. One major challenge was accommodating multiple types of contacts (`Person`, `Staff`, `Maintainer`, `Supplier`) while ensuring compatibility with the original AB3 data model and commands. This required thorough understanding of the project structure and meticulous modification of existing components, particularly the `JsonAdaptedPerson` classes. + +Additionally, redesigning command formats and implementing new features such as dynamic search, sorting, note/reminder functionalities, and pin/unpin features demanded careful planning and detailed implementation. Adapting the undo and redo features from AB4 to fit within the AB3 framework posed another challenge, as it necessitated significant modifications to `ModelManager` and command execution flow while ensuring backward compatibility. + +#### Effort Required +The effort required for the project was substantial, spanning analysis, design, development, testing, and documentation phases. The multidisciplinary team invested significant time and resources in understanding AB3's architecture, identifying areas for enhancement, and implementing new features while ensuring compatibility and stability. Agile methodologies were employed to iteratively address challenges and incorporate stakeholder feedback, resulting in an efficient developmental process. + +#### Achievements +Despite the challenges, the project achieved several milestones that significantly enhanced the contact management system's functionality and user experience. Key achievements included: + +1. Successful integration of multiple contact types, providing users with greater flexibility and organizational capabilities. +2. Redesigning command formats for improved intuitiveness and ease of use, enhancing user interaction. +3. Implementation of dynamic search and sorting functionalities, empowering users to efficiently navigate and manage their contacts. +4. Introduction of note and reminder features, enabling users to add context and schedule tasks associated with contacts. +5. Seamless integration of pin and unpin functionalities, allowing users to prioritize contacts. +6. Seamless integration of undo and redo functionalities, allowing users to navigate between different states of their contacts list, improving data integrity. + + +#### Effort Saved Through Reuse: +Approximately 10% of the project effort was saved through strategic reuse of existing components and libraries. Notably, the redesign of command formats leveraged insights from previous projects and industry best practices, streamlining development and ensuring consistency. Additionally, adapting the undo and redo features from AB4 involved reusing core concepts and methodologies, significantly reducing implementation complexity and effort. -### Saving data +In summary, the project's successful implementation of advanced features within the AB3 framework demonstrates our team's proficiency in software development and problem-solving. Despite the inherent challenges, our strategic approach to reuse and adaptation resulted in a robust and feature-rich contact management system that meets the evolving needs of users. -1. Dealing with missing/corrupted data files +
- 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ +### Acknowledgements +1. PoochPlanner is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org/). +2. The undo and redo features (including the design and UML diagrams) was inspired and reused with minimal changes from [SE-addressbook](https://se-education.org/addressbook-level4/DeveloperGuide.html#undo-redo-feature). -1. _{ more test cases …​ }_ diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 7abd1984218..9b7a9c2762f 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -3,196 +3,1147 @@ 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. +
+
+ Add before +
+
+ +
+ +## Table of Contents * Table of Contents {:toc} --------------------------------------------------------------------------------------------------------------------- + +
+ +## Welcome to PoochPlanner + +**PoochPlanner** is the quintessential address book, crafted exclusively for ***dog cafe owners***. We understand the unique challenges you face, which is why PoochPlanner comes packed with features designed specifically to simplify contact management for dog cafe owners like yourself. + +By seamlessly combining the efficiency of a Command Line Interface (CLI) with an intuitive Graphical User Interface (GUI), PoochPlanner empowers you to effortlessly complete your contact management tasks twice as fast as traditional GUI applications. + +Get started by following the steps in this user guide! Experience the difference today with PoochPlanner — the ultimate solution tailored to your needs. + +## Introducing PoochPlanner +PoochPlanner is your go-to address book, tailor-made for dog cafe owners like yourself. With four main contact types — **person, staff, supplier, maintainer** — each equipped with its own set of attributes and functions, managing your contacts has never been easier. + +Contact | Definition and Examples +--------|------------------ +Person | Any person that does not fall under staff, supplier, or maintainer.
E.g. neighbor, work friend +Staff | Any full-time or part-time employee working in your dog cafe.
E.g. waiter, waitress, dishwasher +Supplier | Any logistics partner that supplies stock for your dog cafe.
E.g. kibbles supplier, coffee beans supplier +Maintainer | Any externally hired specialized personnel that takes care of the dogs.
E.g. dog groomer, veterinarian + +
+ +But our commitment to improving your contact management experience doesn't end here. In addition to the essential functionalities such as adding contacts, searching for contacts, editing contacts, deleting contacts, viewing help menus, listing all contacts, clearing all contacts, undoing commands, and redoing commands, **PoochPlanner** comes loaded with extra features. You can now pin important contacts, jot down notes, set reminders, rate, and even sort your contacts - all to ensure a seamless and efficient workflow tailored to your needs as a busy dog cafe owner. + +Feature | Purpose +--------|------------------ +Pin/Unpin | Pin/Unpin contacts so that you can easily access your important contacts. +Note | Add notes to contacts so that you can keep track of all of your important details. +Remind | View reminders of upcoming deadlines so that you will never miss any of your tasks. +Rate | Add ratings to your contacts so that you can easily track the performances of your contacts. +Sort | View your contacts in a sorted list so that you can easily filter and locate your contacts. + +
+ +## Navigating this User Guide +This user guide is designed to help you easily navigate the commands of PoochPlanner and make the most of this tool, whether you are a beginner or an experienced user. + +- If you are eager to get started with PoochPlanner, head over to our **[Quick Start](https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html#quick-start)** section for easy-to-follow instructions on downloading PoochPlanner. +- If you are a new user, explore the following sections to familiarize yourself with the basics:
+ 1. **[Navigating the GUI](https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html#navigating-the-gui)** : Learn about the input box and contact cards.
+ 2. **[Features](https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html#features)** : Discover the exciting features waiting for you.
+- If you are an advanced user, dive into our special features to further enhance your experience: + 1. **[Rate](https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html#rating-a-contact--rate)** : Add performance ratings to your contacts.
+ 2. **[Pin](https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html#pinning-a-contact--pin)** and **[Unpin](https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html#unpinning-a-contact--unpin)** : Keep your frequent contacts accessible.
+ 3. **[Sort](https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html#sorting-the-address-book--sort)** : Organize your contacts based on your preferences.
+ 4. **[Note](https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html#adding-a-note--note)** : Add notes to your contacts.
+ 5. **[Remind](https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html#viewing-reminders--remind)** : View your contacts with notes containing incoming deadlines.
+- If you want a quick overview of our commands, check out our **[Command Summary](https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html#command-summary)**. +- If you are encountering issues or have any questions about PoochPlanner, check out our **[Common Errors](https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html#command-summary)** and **[FAQ](https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html#faq)** for helpful troubleshooting tips and answers to the common queries. + +
+ +## Useful Notations and Glossary + +While exploring PoochPlanner’s features with this user guide, you can take note of the following symbols and what they represent. + +Symbol | Meaning +--------|------------------ +:information_source: | Important information +:exclamation: | Warning or caution +:bulb: | Additional information such as tips or notes + +The following glossary table provides clarification on commonly used terminology. + +Phrases | Meaning +--------|------------------ +GUI | GUI stands for Graphical User Interface and it represents the visual display of PoochPlanner that you see when PoochPlanner is running. +GUI component | GUI components are parts that make up a GUI. For more information on specific GUI components, refer to this [section](#navigating-the-gui). +CLI | CLI stands for Command Line Interface and it represents a text-based user interface to interact with PoochPlanner. +Command | Command refers to an input from the user that informs PoochPlanner to perform a specified action. View PoochPlanner’s [Command Summary](#command-summary). +Prefix | Prefixes are fields that come in a specified format (e.g. "; name :", "; phone :"). View PoochPlanner’s [Prefix Summary](#prefix-summary). +Case-sensitive | The casing of the alphabetic characters matters (e.g. “good” is different from “GOOD”). +Case-insensitive | The casing of the alphabetic characters does not matter (e.g. “good” is taken to be equivalent to “GOOD”). +Space-sensitive | The number of spaces in a command matters (e.g. “happy puppy” is different from “happypuppy”). +Space-insensitive | The number of spaces in a command does not matter (e.g. “happy puppy” is taken to be equivalent to “happypuppy”). +Delimiter | A separator in the command input that defines the boundaries between different information in a command (e.g. ";" is a delimiter for our commands). +Contact card | The box displayed for each contact which contains all of their respective details. + + +
+ ## Quick start -1. Ensure you have Java `11` or above installed in your Computer. +1. Ensure that you have Java `11` installed on your computer. + +2. Download the latest `[CS2103T-W10-2][PoochPlanner].jar` from [here](https://github.com/AY2324S2-CS2103T-W10-2/tp/releases). -1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +3. Copy the file to the folder that you want to use as the _home folder_ for your PoochPlanner. -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +4. Open a command terminal and navigate to the folder where you placed the jar file in. -1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.jar` command to run the application.
- A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.png) +5. Use the command `java -jar [CS2103T-W10-2][PoochPlanner].jar` to run PoochPlanner.
-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. +
+
+ Add before + A GUI similar to the one above should appear in a few seconds. +
+ Since PoochPlanner does not contain any sample data, an empty contacts list will be shown on first use. +
+
- * `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. +
- * `delete 3` : Deletes the 3rd contact shown in the current list. +6. You can try out the sample commands below.
- * `clear` : Deletes all contacts. + * `/add-person ; name : Pooch ; phone : 98765435 ; address : Poochie Street 24 ; email : iampooch@gmail.com`
+ The above command adds a general contact named `Pooch` to your contacts list in PoochPlanner. - * `exit` : Exits the app. + * `/delete ; name : Pooch`
+ The above command deletes a contact named `Pooch` from your contacts list in PoochPlanner. -1. Refer to the [Features](#features) below for details of each command. + * `/exit` : Exits PoochPlanner. --------------------------------------------------------------------------------------------------------------------- +7. Refer to our [Features](#features) section below for the details of each command. -## Features +[//]: # (@@author jannaleong) +
+ +## Navigating the GUI + +PoochPlanner has a Graphical User Interface (GUI) that provides you with a pleasant visual experience. +Here is a quick look at the different GUI components of PoochPlanner. + +### Basic Orientation + +![Quick Orientation](images/ug-images/BasicGuiNavigation.png) +### Contact Card + +![Employee Card](images/ug-images/PersonCardGuiNavigation.png) + + +
+ +[//]: # (@@author) + +## Features
**:information_source: Notes about the command format:**
-* 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`. +* Words in `[parameter]` are the parameter values to be keyed in by you.
+ For instance, in `/add-staff ; name : [name]`, you will need to key in a value for the `[name]` parameter.
+ For example, the actual command that you may input can be `/add-staff ; name : Poochie`. -* 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`. +* Parameters can be in any order.
+ For instance, if the command specifies `address : [address] ; phone : [phone]`, `phone : [phone] ; address : [address]` is also equivalent. -* 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. +* You should be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to PoochPlanner. -* 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. +* You should take caution when entering the commands as commands are space-sensitive.
+ For instance, both ` ; address : ` and ` ; phone : ` are correctly spaced prefixes.
+ Make sure that all prefixes that you have keyed in contain the correct spacing format.
+ +
+ +
-* 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`. +### Core Features -* If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application. +#### Adding a contact: `add` +[//]: # (@@author jannaleong) +Adds a **person / staff / supplier / maintainer** contact so that your contacts list can be updated with new contacts. + +The table below summarizes the `add` command, format, and examples for each contact type. + +| Adds a ... | Format & Examples | +|------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Person** | Format :
`/add-person ; name : [name] ; phone : [phone] ; address : [address] ; email : [email]`

Example:
`/add-person ; name : Janna ; phone : 98765435 ; address : Poochie Street 24 ; email : ihelppooches@gmail.com`

The above command adds a `Person` contact. Person's name is **_Janna_**, phone number is **_98765435_**, address is **_Poochie Street 24_**, and email is **_ihelppooches@gmail.com_**. | +| **Staff** | Format :
`/add-staff ; name : [name] ; phone : [phone] ; address : [address] ; email : [email] ; salary : [salary/hr] ; employment : [part-time/full-time]`

Example:
`/add-staff ; name : Poochie ; phone : 98765435 ; address : Poochie Street 21 ; email : ilovecatstoo@gmail.com ; salary : $50/hr ; employment : part-time`

The above command adds a `Staff` contact. Staff's name is **_Poochie_**, phone number is **_98765435_**, address is **_Poochie Street 21_**, email is **_ilovecatstoo@gmail.com_**, salary is **_$50/hr_**, and employment is **_part-time_**. | +| **Supplier** | Format :
`/add-supplier ; name : [name] ; phone : [phone] ; address : [address] ; email : [email] ; product : [product] ; price : [price/(quantity)]`

Example:
`/add-supplier ; name : PetCo ; phone : 98673098 ; address : Meow Street 24 ; email : ilovewombatstoo@gmail.com ; product : kibble ; price : $98/bag`

The above command adds a `Supplier` contact. Supplier's name is **_Petco_**, phone number is **_98673098_**, address is **_Meow Street 24_**, email is **_ilovewombatstoo@gmail.com_**, product is **_kibble_**, and price of product is **_$98/bag_**. | +| **Maintainer** | Format :
`/add-maintainer ; name : [name] ; phone : [phone] ; address : [address] ; email : [email] ; skill : [skill] ; commission : [commission/hr]`

Example:
`/add-maintainer ; name : Tom Tan ; phone : 98765435 ; address : Poochie Street 24 ; email : ihelppooches@gmail.com ; skill : trainer ; commission : $60/hr`

The above command adds a `Maintainer` contact. Maintainer's name is **_Tom Tan_**, phone number is **_98765435_**, address is **_Poochie Street 24_**, email is **_ihelppooches@gmail.com_**, skill is **_trainer_**, and commission is **_$60/hr_**. | + +
+ +[//]: # (@@author) + +
+
+ Add before + Above is an example of adding a person contact named "Janna". +
+ Input: /add-person ; name : Janna ; phone : 98765435 ; address : Poochie Street 24 ; email : ihelppooches@gmail.com +
+ Output: Adds a contact named "Janna" to your contacts list. +
+
+ +
+ +
:exclamation: **Constraints:**
+ +* Contact type (person / staff / supplier / maintainer) must match the command used. `/add-person`, `/add-staff`, `/add-supplier` and `/add-maintainer` respectively.
+ +* The fields `salary` and `employment` are only for Staff contact type.
+ +* The fields `product` and `price` are only for Supplier contact type.
+ +* The fields `skill` and `commission` are only for Maintainer contact type.
+ +* Adding duplicate names will not be allowed.
+ +* `name` is case-insensitive and space-sensitive.
+ +* `email` must in the format {example}@{domain} or {example}@{domain}.{extension}.
+ +* `phone` must be in the format {Number} and at least 3 digits long.
+ +* Only one phone number is allowed to be inserted per contact.
+ +* `salary` and `commission` must be in the format ${Number}/hr.
+ +* `price` must be in the format ${Number}/{quantity}.
+ +* `employment` must be either "part-time" or "full-time".
-### Viewing help : `help` +
:bulb: **Tip:**
-Shows a message explaning how to access the help page. +* You can use the `/undo` command to undo an `/add-XYZ` command so that you can revert your changes!
-![help message](images/helpMessage.png) +
+
-Format: `help` +#### Editing a contact : `edit` +Edits a **person / staff / supplier / maintainer** contact so that you can consistently modify and update your contacts list in PoochPlanner with new details. -### Adding a person: `add` +The table below summarizes the `edit` command, format, and examples for each contact type. -Adds a person to the address book. +| Edits a ... | Format & Examples | +|----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Person** | Format :
`/edit-person ; name : [name] ; field : { phone : [phone] ; address : [address] ; email : [email] }`

Example:
`/edit-person ; name : Mochie ; field : { address : Pooch Street 31}`

The above command edits the `address` field of `Mochie` to `Pooch Street 31`. | +| **Staff** | Format :
`/edit-staff ; name : [name] ; field : { phone : [phone] ; address : [address] ; email : [email] ; salary : [salary] ; employment : [part-time/full-time] }`

Example:
`/edit-staff ; name : Thomas ; field : { address : Poochie Street 25 ; employment : full-time }`

The above command edits the `address` and `employment` fields of `Thomas` to `Poochie Street 25` and `full-time` respectively. | +| **Supplier** | Format :
`/edit-supplier ; name : [name] ; field : { phone : [phone] ; address : [address] ; email : [email] ; product : [product] ; price : [price] }`

Example:
`/edit-supplier ; name : Rachel ; field : { product : kibble ; price : $75/bag}`

The above command edits the `product` and `price` fields of `Rachel` to `kibble` and `$75/bag` respectively. | +| **Maintainer** | Format :
`/edit-maintainer ; name : [name] ; field : { phone : [phone] ; address : [address] ; email : [email] ; skill : [skill] ; commission : [commission] }`

Example:
`/edit-maintainer ; name : Alice ; field : { commission : $10/hr}`

The above command edits the `commission` field of `Alice` to `$10/hr`. | -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +
-
:bulb: **Tip:** -A person can have any number of tags (including 0) +
+
+ Add before + Above is an example of editing a person contact named "Janna". +
+ Input: /edit-person ; name : Janna ; field : { address : Pooch Street 31 } +
+Output: Edits the address field of the contact named "Janna" in your contacts list to "Pooch Street 31". +
-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` +
-### Listing all persons : `list` +
:exclamation: **Constraints:**
-Shows a list of all persons in the address book. +* Contact type (person / staff / supplier / maintainer), must match the command used. `/edit-person`, `/edit-staff`, `/edit-supplier`, and `/edit-maintainer` respectively.
-Format: `list` +* `name` is a compulsory field that is case-insensitive and space-sensitive.
-### Editing a person : `edit` +* A contact with the specified `name` must be present in your contacts list.
-Edits an existing person in the address book. +* The fields `salary` and `employment` are only for Staff contact type.
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +* The fields `product` and `price` are only for Supplier contact type.
-* 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. +* The fields `skill` and `commission` are only for Maintainer contact type.
-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. +* At least one field must be provided.
-### Locating persons by name: `find` +* `phone` must be in the format {Number} and at least 3 digits long.
-Finds persons whose names contain any of the given keywords. +* `email` must in the format {example}@{domain} or {example}@{domain}.{extension}.
-Format: `find KEYWORD [MORE_KEYWORDS]` +* `salary` and `commission` must be in the format ${Number}/hr.
-* 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` +* `price` must be in the format ${Number}/{quantity}.
-Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +* `employment` must be either "part-time" or "full-time".
+
+ +
:bulb: **Tip:**
-### Deleting a person : `delete` +* You can edit multiple fields simultaneously to save time!
-Deletes the specified person from the address book. +
-Format: `delete INDEX` +
+ +#### Searching a contact : `search` + +Filters a **person / staff / supplier / maintainer** contact in your contacts list in PoochPlanner so that you can locate your contacts easily. + +Format:
+`/search ; [search-field] : [value]` + +Examples:
+* `/search ; phone : 98765432` + + The above command searches for all contacts with the phone number `98765432`. + + +* `/search ; salary : $50/hr` + + The above command searches for all staff with a salary `$50/hr`. + + +* `/search ; name : Poochie ; phone : 98765432` + + The above command searches for all contacts with the name `Poochie` and the phone number `98765432`. + +
+ +
+
+ Add before + Above is an example of searching for a contact named "Tom". +
+ Input: /search ; name : Tom +
+ Output: Finds all contacts that contain "Tom" in their names in your contacts list. +
+
+ +
+ + +
:exclamation: **Constraints:**
+ +* Any valid fields, such as `name`, `phone`, `address`, `email`, `salary`, `employment`, `price`, `product`, `skill`, `commission`, `tag` or `note`, can be provided as a search field.
+ +* Multiple search fields may be provided.
+ +* Queries are case-insensitive and space-sensitive.
+ +* Duplicate entries for the same fields are not allowed. For instance, `/search ; name : Janna ; name : Poochie` is not allowed.
+ +
+
+
:bulb: **Tips:**
-* 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, …​ +* You can use `/list` to see your full contacts list again after searching for a contact!
-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. +If you would like to search for the exact `salary`/`price`/`commission`, you may follow this format:
-### Clearing all entries : `clear` +* To search for the exact `salary` and `commission`, your input must be in the format ${Number}/hr.
-Clears all entries from the address book. +* To search for the exact `price`, your input must be in the format ${Number}/{quantity}.
-Format: `clear` +If you would like to search for `salaries`/`prices`/`commissions` within a specified range, you may follow this format:
+ +* To search for a range of values for `salaries`/`prices`/`commissions`, your input must be in the format ${Number}.
+ +* For instance, if you would like to search for all salaries within the range `$50-59/hr`, you may key in `/search ; salary : $5`, with `$5` as the search query. This displays all staff that are paid from `$50/hr` to `$59/hr`.
+ +* Note that this query also searches for staff with salaries of exactly `$5/hr`, from `$500/hr` to `$599/hr` as well, and even in larger ranges.
+ +
+ + +
+ +#### Deleting a contact : `delete` + +Deletes a **person / staff / supplier / maintainer** contact from your contacts list in PoochPlanner so that you can remove your outdated contacts from PoochPlanner. + +Format:
+`/delete ; name : [value]` + +Example:
+* `/delete ; name : Poochie` + + The above command deletes the contact named `Poochie`, provided `Poochie` exists in your contacts list. + +
+ +
+
+ Add before + Above is an example of deleting a contact named "Janna". +
+ Input: /delete ; name : Janna +
+ Output: Deletes the contact named "Janna" from your contacts list. +
+
+ +
+ +
:exclamation: **Constraints:**
+ +* `name` is a compulsory field that is case-insensitive and space-sensitive.
+ +* A contact with the specified `name` must be present in your contacts list.
+
+ +
+ +### Special Features +#### Rating a Contact : `rate` + +Gives a performance rating to a **person / staff / supplier / maintainer** contact from your contacts list in PoochPlanner so that you can track the performance of your contacts. + +Format:
+`/rate ; name : [name] ; rating : [rating value from 0-5]` + +Example: +* `/rate ; name : Poochie ; rating : 3` + + The above command rates the contact named `Poochie` with a rating of `3`, provided a contact with the name `Poochie` exists in your contacts list. + +
+ +
+
+ Add before + Above is an example of rating a person contact named "Janna". +
+ Input: /rate ; name : Janna ; rating : 5 +
+ Output: Rates the contact named "Janna" with a rating of 5 in your contacts list. +
+
+ +
+ +
:exclamation: **Constraints:**
+ +* `rating` can only accept whole number values from `0` to `5` inclusive.
+ +* A contact with the specified `name` must be present in your contacts list.
+ +* `name` and `rating` are compulsory fields that are both case-insensitive and space-sensitive.
+ +* A `rating` of `0` is equivalent to no rating given and will not display any rating.
+ +
+
+
:bulb: **Tip:**
+ +* You can set a contact's `rating` to `0` to reset its rating!
+ +
+ +
+ +#### Pinning a contact : `pin` + +Pins a **person / staff / supplier / maintainer** contact so that your important contacts will always appear at the top of your contacts list in PoochPlanner. + +Format:
+`/pin ; name : [name]` + +Example:
+* `/pin ; name : Poochie` + + The above command pins the contact named `Poochie`, provided a contact with the name `Poochie` exists in your contacts list. + +
+ +
+
+ Add before + Above is an example of pinning a person contact named "Tom". +
+ Input: /pin ; name : Tom +
+ Output: Pins the contact named "Tom" in your contacts list. +
+
+ +
+ + +
:exclamation: **Constraints:**
+ +* `name` is a compulsory field that is case-insensitive and space-sensitive.
+ +* A contact with the specified `name` must be present in your contacts list.
+ +* Using the `pin` command on a contact that has been pinned does not make any changes to your contacts list and simply repins the same contact successfully.
+ +* A contact will remain pinned if you use the `undo` command once on a contact that has been pinned twice or more.
+
+ +
:bulb: **Tips:**
+ +* Use the `pin` command for your frequent contacts!
+ +* For your convenience, if you have accidentally made a typo in the name value, you may conveniently rectify your mistake by retyping the name prefix and name value in your command as only the latest field will be processed.
+ +
+ +
+ +#### Unpinning a contact : `unpin` + +Unpins a **person / staff / supplier / maintainer** contact so that your less important contacts can be removed from the top of your contacts list in PoochPlanner. + +Format:
+`/unpin ; name : [name]` + +Example:
+* `/unpin ; name : Poochie` + + The above command unpins the contact named `Poochie`, provided a contact named `Poochie` exists in your contacts list. + +
+ +
+
+ Add before + Above is an example of unpinning a person contact named "Tom". +
+ Input: /unpin ; name : Tom +
+ Output: Unpins the contact named "Tom" in your contacts list. +
+
+ +
+ +
:exclamation: **Constraints:**
+ +* A contact with the specified `name` must be present in your contacts list.
+ +* Using the `pin` command on a contact that has been pinned does not make any changes to your contacts list and simply repins the same contact successfully.
+ +* `name` is a compulsory field that is case-insensitive and space-sensitive.
+ +* A contact with the specified `name` must be present in your contacts list.
+ +* Using the `unpin` command on a contact that is already unpinned does not make any changes to your contacts list and simply unpins the same contact successfully.
+ +* A contact will remain unpinned if you use the `undo` command once on a contact that has been unpinned twice or more.
+
+ +
:bulb: **Tip:**
+ +* For your convenience, if you have accidentally made a typo in the name value, you may conveniently rectify your mistake by retyping the name prefix and name value in your command as only the latest field will be processed.
+ +
+ +
+ +#### Sorting the address book : `sort` + +Sort the address book by a target field in lexicographical order so that you can display all of your contacts neatly and locate them easily. + +Format:
+`/sort ; field : [target-field]` + +Example:
+* `/sort ; field : name` + + The above command sorts the contacts by `name` in lexicographical order. + +
+ +
+
+ Add before + Above is an example of sorting your contacts list by the name field. +
+ Input: /sort ; field : name +
+ Output: Sorts all of your contacts by name in lexicographical order. +
+
+ +
+ +
:exclamation: **Constraints:**
+ +* This command sorts by specifying a valid field, such as `name`, `phone`, `email`, `address`, `salary`, `employment`, `price`, `product`, `skill`, `commission`, `tag` or `note`.
+ +* All field values are case-insensitive.
+ +* At least one field must be provided.
+ +
+ +
:bulb: **Tip:**
+ +* For your convenience, if you have accidentally made a typo in the field value, you may conveniently rectify your mistake by retyping the corresponding prefix and field value in your command as only the latest field will be processed.
+ +
+ +
+ +[//]: # (@@author jannaleong) + +#### Adding a note : `note` + +Adds a note to a specified person in your PoochPlanner so that you can keep track of any additional details regarding your contacts. +You may also specify an optional deadline for the note. + +Formats:
+`/note ; name : [name] ; note : [note message]` +
+`/note ; name : [name] ; note : [note message] ; deadline : [date]` + +Examples:
+ +Add a note without a deadline:
+* `/note ; name : Moochie ; note : get 10kg of matcha from moochie` + + The above command adds the note "get 10kg of matcha from moochie" to the contact with name `Moochie`. Note that this note has no deadline. + +Add a note with a deadline:
+* `/note ; name : Moochie ; note : get 10kg of matcha from moochie ; deadline : 2020-10-10` + + The above command adds the note "get 10kg of matcha from moochie" to the contact with name `Moochie`. Note that this note has a deadline set to `Oct 10 2020`. + +
+ +
+
+ Add before + Above is an example of adding a note to a contact named "Ben". +
+ Input: /note ; name : Ben ; note : pay his salary of $1500 ; deadline : 2024-05-20 +
+ Output: Adds a note with a deadline to the contact named "Ben" in your contacts list. +
+
+ +
+ +
:exclamation: **Constraints:**
+ +* `name` and `note` are compulsory fields.
+ +* `deadline` is an optional field.
+ +* `deadline` must follow "YYYY-MM-DD" format.
+ +* `name` is case-insensitive and space-sensitive.
+ +* A contact with the specified `name` must be present in your contacts list.
+ +* `note` can take any non-empty string. + +
+ +
:bulb: **Tips:**
+ +* Notes added are intended to be short details.
+ +* To remove a note, specify in the note field `No note here`. **This note is case-sensitive**.
+ +* For your convenience, if you have accidentally made a typo in the field value, you may conveniently rectify your mistake by retyping the corresponding prefix and field value in your command as only the latest field will be processed.
+ +* Refrain from using `;` in your note. The use of `;` makes the command difficult to parse as `;` is used as a delimiter. This may result in your note not being captured. + +
+ +
+ +[//]: # (@@author) + +#### Undoing a command : `undo` + +Undoes the most recent action so that you can easily revert the changes that you made in PoochPlanner. + +Format:
+`/undo` + +Example: +* `/undo` + + The above command undoes the most recent command. + + +
+Below is an example of undoing an `add` command: + +
+
+ Add before + Input: /add-person ; name : Tom ; phone : 99983932 ; address : Poochie Street 20 ; email : icleanpooches@gmail.com +
+ Output: Add a contact named "Tom" to your contacts list. +
+
+ +
+ +
+ +
+
+ Add before + Input: /undo +
+ Output: Undoes the add command above for the contact named "Tom". +
+
+ +
+ +
:exclamation: **Constraints:**
+ +* There are no additional fields required for this command.
+ +* Any unnecessary parameter or value after `/undo` will simply be ignored.
+ +* This command can only be executed when at least one change has been made.
+
+ +
:bulb: **Tips:**
+ +* `/undo` works on all commands that have modified your contacts list.
+ +* `/undo` will not work on commands that do not modify your contacts list such as `/search`, `/list`, and `/help` commands!
+ +
+ +
+ +#### Redoing a command : `redo` + +Redoes the most recent undo action so that you can easily revert any unintentional uses of the `undo` command that you made in PoochPlanner. + +Format:
+`/redo` + +Example:
+* `/redo` + + The above command redoes the most recent `undo` command. + +
+Below is an example of redoing an add command : + +
+
+ Add before + Input: /undo +
+ Output: Undoes the add command for the contact named "Tom". +
+
+ +
+ +
+ +
+
+ Add before + Input: /redo +
+ Output: Redoes the add command for the contact named "Tom". +
+
+ +
+ +
:exclamation: **Constraints:**
+ +* There are no additional fields required for this command.
+ +* Any unnecessary parameter or value after `/redo` will simply be ignored.
+ +* This command can only be executed when at least one `/undo` command has been executed.
+ +
+ +
+ +
:bulb: **Tips:**
+ +* `/redo` works on all commands that modify your contacts list.
+ +* `/redo` will not work on commands that do not modify your contacts list such as `/search`, `/list`, and `/help` commands!
+ +
+ +
+ +[//]: # (@@author jannaleong) + +#### Viewing help : `help` + +Shows a help message of how to use commands so that you can get help regarding the commands easily. + +Format:
+`/help ; command : [command type]` + +Examples:
+* `/help ; command : delete` +
+ The above command provides help for the `delete` command. + +* `/help ; command : add` +
+ The above command provides help for the `add` command. + +
+ +
+
+ Add before + Above is an example of getting help for the add command. +
+ Input: /help ; command : add +
+ Output: Shows a pop-up help window for the add command. +
+
+ +
+ +
:exclamation: **Constraints:**
+ +* `command` is a compulsory field.
+ +* Help is given for all valid commands in PoochPlanner. Valid command inputs include `general`, `add`, `clear`, `delete`, `edit`, + `exit`, `list`, `note`, `pin`, `unpin`, `undo`, `redo`, `rate`, `remind`, `search`, `sort`. +
+ +
:bulb: **Tips:**
+ +* If you wish to get help for all commands, enter the command input as `general` to open the general help window.
+ +* For your convenience, if you have accidentally made a typo in the field value, you may conveniently rectify your mistake by retyping the corresponding prefix and field value in your command as only the latest field will be processed.
+
+ +
+ +#### Viewing reminders : `remind` + +Shows all contacts with note deadlines from today onwards so that you can view all your upcoming deadlines easily. + +Format:
+`/remind` + +Example:
+* `/remind` +
+ The above command displays all contacts with note deadlines from today onwards. + +
+ +
+
+ Add before + Above is an example of getting a reminder of a contact with a specified note deadline after today. +
+ Input: /remind +
+ Output: Shows all contacts with note deadlines from today onwards. +
+
+ +
+ +
:bulb: **Tips:**
+ +* Specifying `/remind [any text]` is equivalent to `/remind`.
+ +* To revert back to your full contacts list, you can use the command `/list`. + +
+ + +
+ +#### Clearing all contacts : `clear` + +Remove all contacts from your contacts list so that you can reset your contacts list easily. + +Format:
+`/clear` + +Example:
+* `/clear` +
+ The above command clears all contacts from your contacts list. + +
+ +
+
+ Add before + Above is an example of clearing your contacts list. +
+ Input: /clear +
+ Output: Clears all contacts from your contacts list. +
+
+ +
+ +
:bulb: **Tip:**
+ +* To revert to your full contacts list, you can use the command `/undo`. + +
+ +
+ +#### Listing all contacts : `list` + +Lists all contacts in your PoochPlanner so that you can view all of your contacts in PoochPlanner at once. + +Format:
+`/list` + +Example:
+* `/list` +
+ The above command lists all contacts in your contacts list. + +
+ +
+
+ Add before + Above is an example of listing all contacts. +
+ Input: /list +
+ Output: Lists all contacts from your contacts list. +
+
+ +
+ +
:bulb: **Tip:**
+ +* To revert to your full contacts list after using any filtering commands such as `/search`, you can use the command `/list`. + +
+ +[//]: # (@@author) + +
### Exiting the program : `exit` -Exits the program. +Exits your PoochPlanner application. -Format: `exit` +Format: `/exit` ### Saving the data -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +Data in your PoochPlanner is saved in the hard disk automatically after any command that modifies the data. You will not need to save any data manually. ### Editing the data file -AddressBook data are saved automatically as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file. +Data in your PoochPlanner is saved automatically as a JSON file `[JAR file location]/data/poochplanner.json`. Advanced users are welcome to update data directly by editing their `poochplanner.json` file. + +
:exclamation: **Caution:**
+ +Manually editing the JSON file is not advised and should be taken with extreme caution.
If your changes to the data file make its format invalid, your PoochPlanner may discard all data and start with an empty data file on the next run.
Hence, it is recommended that you make a backup of the file before editing it.
+Furthermore, certain manual edits that you make can cause your PoochPlanner to behave in unexpected ways (e.g. if a value entered lies outside its acceptable range). +
+ +
+ +[//]: # (@@author jannaleong) +## Common Errors +### Unknown Command + +If you encounter an unknown command error, please ensure that the command you entered is a valid command mentioned +in our [Command Summary](#command-summary). Below is an example of an unknown command input and the corresponding error message. +
+ +
+
+ Add before + The above image depicts the unknown command error message caused by an unknown command. +
+
+
+ +### Invalid Field + +If you encounter an invalid field error, please ensure that the field you entered is a valid field for the command you +are using. Refer to our [Command Summary](#command-summary) for a list of valid fields for each PoochPlanner command.
Below is an example of a command with an invalid field and the corresponding error message. +
-
: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. Hence, it is recommended to take a backup of the file before editing it.
-Furthermore, certain edits can cause the AddressBook to behave in unexpected ways (e.g., if a value entered is outside of the acceptable range). Therefore, edit the data file only if you are confident that you can update it correctly. +
+
+ Add before + The above image depicts an invalid field error message caused by a help command with an invalid field. +
+
+ +### Missing Field -### Archiving data files `[coming in v2.0]` +If you encounter a missing field error, please ensure that the fields listed in the error message are present in your command input. Refer to our [Command Summary](#command-summary) for a list of fields required for all PoochPlanner commands. Below is an example of a command with a missing field and the corresponding error message. +
-_Details coming soon ..._ +
+
+ Add before + The above image depicts a missing field error caused by a note command with missing fields for name and note. +
+
+
--------------------------------------------------------------------------------------------------------------------- +[//]: # (@@author) ## FAQ +### Launching PoochPlanner -**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. +**Q**: How can I launch PoochPlanner if clicking on the JAR file does not work?
+**A**: There are two possible methods to launch PoochPlanner. --------------------------------------------------------------------------------------------------------------------- +**Method 1**: Using the Command Line +1. Open the command line in your operating system's terminal software. +2. Navigate to the directory where the JAR file is located. +3. Enter `java -jar [CS2103T-W10-2][PoochPlanner].jar` and the PoochPlanner Application should launch. -## Known issues +**Method 2**: Using `.bat`/`.sh` Scripts +1. Create a new text file and paste the following line into the file: + ``` + java -jar [JAR file location]/[CS2103T-W10-2][PoochPlanner].jar + ``` +2. Save the file as `poochplanner.bat` (Windows) or `poochplanner.sh` (macOS/Linux). +3. Change the admin settings of the script to allow it to run as a program: + - Windows: Right-click on the script and select Properties. Under General, check the box that says `Allow this file to run as a program`. + - macOS/Linux: Open the Terminal and navigate to the directory where the script is located. Type `chmod +x [script name]` and press `Enter`.
Note: (`chmod +x` changes permissions of the script to allow it to be executed) +4. Double-click on the script to launch PoochPlanner. -1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again. +### Checking Java Version --------------------------------------------------------------------------------------------------------------------- +**Q**: How can I check my Java version?
+**A**: You can open a command line and type `java -version`. If you do not have Java installed, you can install Java 11 using the Oracle guide [here](https://www.oracle.com/java/technologies/downloads/#java11). Alternatively, you can install the OpenJDK version. For macOS users, you may wish to follow the instructions [here](https://nus-cs2103-ay2324s1.github.io/website/admin/programmingLanguages.html). -## 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` +### Loading Data from Another Computer + +**Q**: How can I transfer my PoochPlanner contacts to another computer?
+**A**: You can install PoochPlanner in your target computer and overwrite the empty data file it creates with the file that contains the data of your previous PoochPlanner by copying the contents of the `poochplanner.json` file from your current computer and pasting it inside the `poochplanner.json` file of your target computer. + +### Using PoochPlanner + +**Q**: What are the available commands in PoochPlanner?
+**A**: Please refer to our [Command Summary](#command-summary) for the list of available commands. + +**Q**: Do I need an internet connection to use PoochPlanner?
+**A**: All of PoochPlanner’s functionality can be used offline! No internet connection is required. + +**Q**: How do I save my data?
+**A**: Data is saved in the hard disk automatically after any command that changes the data. There is no need to save manually. + +**Q**: If I have a lot of contacts, is there a way for me to access my most frequently used contacts easily?
+**A**: Yes! You may wish to keep your most important contacts at the top of your contacts list. With our `pin` feature, PoochPlanner ensures that you are always able to see them!

The syntax for the `pin` command is as follows: + +`/pin ; name : [name]` + +**Q**: How can I remove a contact's rating?
+**A**: In PoochPlanner, a `0` rating corresponds to a non-rating (no rating is provided). Therefore, to remove a contact's rating, simply give the target contact a rating of `0`.

The syntax for the `rate` command is as follows: + +`/rate ; name : [name] ; rating : [rating]` + +
+ +**Q**: Can I append a note to a contact when creating it?
+**A**: Yes you can! You may add a note to a contact at the time of its creation. Simply append the `; note :` prefix, and you should be on your way!

+Example: Adding a new contact with a note and a rating + +`/add-person ; name : Pooch ; phone : 98883888 ; address : Pooch Street 32 ; email : impooch@gmail.com ; rating : 3 ; note : Hello my name is Pooch! ` + +
:information_source: **Note:** While adding notes is permitted with the `add` command, we highly suggest you to make use of our `note` command if you would like to add a deadline to the note.
+ +
+ +## Known issues + +1. **When using multiple screens**, if you move PoochPlanner to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by your PoochPlanner before running PoochPlanner again. + + +## Prefix Summary + +A **prefix** is an integral construct of PoochPlanner's commands that allow PoochPlanner to uniquely identify contact fields. + +| Prefix | Field | Contact Type | +|------------------|-------------------------------------------------------------|----------------| +| `; name :` | Specifies the name of the contact. | All | +| `; phone :` | Specifies the phone number of the contact. | All | +| `; address :` | Specifies the address of the contact. | All | +| `; email :` | Specifies the email address of the contact. | All | +| `; note :` | Specifies the note appended to the contact. | All | +| `; rating :` | Specifies the rating given to the contact. | All | +| `; deadline :` | Specifies the deadline of the note appended to the contact. | All | +| `; command :` | Specifies the command type to give help for. | All | +| `; salary :` | Specifies the salary of the contact. | Staff | +| `; employment :` | Specifies the employment of the contact. | Staff | +| `; product :` | Specifies the product sold by the contact. | Supplier | +| `; price :` | Specifies the price charged by the contact. | Supplier | +| `; skill :` | Specifies the type of service offered by the contact. | Maintainer | +| `; commission :` | Specifies the commission charged by the contact. | Maintainer | + +
+ +## Command Summary + +| Action | Format and Example | +|---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Add Person** | Format :
`/add-person ; name : [name] ; phone : [phone] ; address : [address] ; email : [email]`

Example :
`/add-person ; name : Janna ; phone : 98765435 ; address : Poochie Street 24 ; email : iamjanna@gmail.com` | +| **Add Staff** | Format :
`/add-staff ; name : [name] ; phone : [phone] ; address : [address] ; email : [email] ; salary : [salary] ; employment : [part-time/full-time]`

Example :
`/add-staff ; name : Poochie ; phone : 98765435 ; address : Poochie Street 21 ; email : ilovecatstoo@gmail.com ; salary : $50/hr ; employment : part-time` | +| **Add Supplier** | Format :
`/add-supplier ; name : [name] ; phone : [phone] ; address : [address] ; email : [email] ; product : [product] ; price : [price]`

Example :
`/add-supplier ; name : PetCo ; phone : 98673098 ; address : Meow Street 24 ; email : ilovewombatstoo@gmail.com ; product : kibble ; price : $98/bag` | +| **Add Maintainer** | Format :
`/add-maintainer ; name : [name] ; phone : [phone] ; address : [address] ; email : [email] ; skill : [skill] ; commission : [commission]`

Example :
`/add-maintainer ; name : Tom Tan ; phone : 98765435 ; address : Poochie Street 24 ; email : ihelppooches@gmail.com ; skill : trainer ; commission : $60/hr` | +| **Edit Person** | Format :
`/edit-person ; name : [name] ; field : { target-field : [value] }`

Example :
`/edit-person ; name : Poochie ; field : { address : Poochie Street 25 }` | +| **Edit Staff** | Format :
`/edit-staff ; name : [name] ; field : { target-field : [value] }`

Example :
`/edit-staff ; name : Poochie ; field : { salary : $40/hr ; employment : part-time }` | +| **Edit Supplier** | Format :
`/edit-supplier ; name : [name] ; field : { target-field : [value] }`

Example :
`/edit-supplier ; name : Poochie ; field : { product : kibble ; price : $75/bag }` | +| **Edit Maintainer** | Format :
`/edit-maintainer ; name : [name] ; field : { target-field : [value] }`

Example :
`/edit-maintainer ; name : Poochie ; field : { commission : $10/hr }` | +| **Delete** | Format :
`/delete ; name : [name] `

Example :
`/delete ; name : Poochie` | +| **Search** | Format :
`/search ; target-field : [value]`

Example :
`/search ; name : Poochie` | +| **List** | Format :
`/list`

Example :
`/list` | +| **Help** | Format :
`/help ; command : [command type]`

Example :
`/help ; command : delete` | +| **Rate** | Format :
`/rate ; name : [name] ; rating : [rating]`

Example :
`/rate ; name : Poochie ; rating : 5` | +| **Undo** | Format :
`/undo`

Example :
`/undo` | +| **Redo** | Format :
`/redo`

Example :
`/redo` | +| **Pin** | Format :
`/pin ; name : [name]`

Example :
`/pin ; name : Poochie` | +| **Unpin** | Format :
`/unpin ; name : [name]`

Example :
`/unpin ; name : Poochie` | +| **Sort** | Format :
`/sort ; field : [target-field]`

Example :
`/sort ; field : name` | +| **Note** | Format :
`/note ; name : [name] ; note : [note]`
or
`/note ; name : [name] ; note : [note] ; deadline : [deadline]`

Example :
`/note ; name : Poochie ; note : get dog for groomers ; deadline : 2020-10-10` | +| **Remind** | Format :
`/remind`

Example :
`/remind` | +| **Exit** | Format :
`/exit`

Example :
`/exit` | diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..4432a069e30 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "PoochPlanner" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2324S2-CS2103T-W10-2/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..2dfb10d3383 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: "PoochPlanner"; font-size: 32px; } } diff --git a/docs/diagrams/AddCommandSequenceDiagram.puml b/docs/diagrams/AddCommandSequenceDiagram.puml new file mode 100644 index 00000000000..be0866fda49 --- /dev/null +++ b/docs/diagrams/AddCommandSequenceDiagram.puml @@ -0,0 +1,69 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":AddCommandParser" as AddCommandParser LOGIC_COLOR +participant "r:AddCommand" as AddCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("/add-person ; name : John Doe ... ") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("/add-person ; name : John Doe ... ") +activate AddressBookParser + +create AddCommandParser +AddressBookParser -> AddCommandParser +activate AddCommandParser + +AddCommandParser --> AddressBookParser +deactivate AddCommandParser + +AddressBookParser -> AddCommandParser : parse("; name : John Doe ... ") +activate AddCommandParser + +create AddCommand +AddCommandParser -> AddCommand +activate AddCommand + +AddCommand --> AddCommandParser : r +deactivate AddCommand + +AddCommandParser --> AddressBookParser : r +deactivate AddCommandParser + +AddCommandParser -[hidden]-> AddressBookParser +destroy AddCommandParser + +AddressBookParser --> LogicManager : r +deactivate AddressBookParser + +LogicManager -> AddCommand : execute(...) +activate AddCommand + +AddCommand -> Model : addPerson(Person) +activate Model + +Model --> AddCommand +deactivate Model + +create CommandResult +AddCommand -> CommandResult +activate CommandResult + +CommandResult --> AddCommand +deactivate CommandResult + +AddCommand --> LogicManager : result +deactivate AddCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index 48b6cc4333c..4de9ea514d4 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -8,13 +8,13 @@ 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 : "/delete ; name : Poochie" activate ui UI_COLOR -ui -[UI_COLOR]> logic : execute("delete 1") +ui -[UI_COLOR]> logic : execute("/delete ; name : Poochie") activate logic LOGIC_COLOR -logic -[LOGIC_COLOR]> model : deletePerson(p) +logic -[LOGIC_COLOR]> model : deletePerson("Poochie") activate model MODEL_COLOR model -[MODEL_COLOR]-> logic diff --git a/docs/diagrams/ClearCommandSequenceDiagram.puml b/docs/diagrams/ClearCommandSequenceDiagram.puml new file mode 100644 index 00000000000..b1464b7ad6d --- /dev/null +++ b/docs/diagrams/ClearCommandSequenceDiagram.puml @@ -0,0 +1,40 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant "r:ClearCommand" as ClearCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute("/clear") +activate LogicManager + + + +LogicManager -> ClearCommand : execute(...) +activate ClearCommand + + +ClearCommand -> Model : setAddressBook() +activate Model + +Model --> ClearCommand +deactivate Model + +create CommandResult +ClearCommand -> CommandResult +activate CommandResult + +CommandResult --> ClearCommand +deactivate CommandResult + +ClearCommand --> LogicManager : result +deactivate ClearCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 5241e79d7da..b374136f589 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -14,10 +14,10 @@ box Model MODEL_COLOR_T1 participant "m:Model" as Model MODEL_COLOR end box -[-> LogicManager : execute("delete 1") +[-> LogicManager : execute("/delete ; name : Poochie") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") +LogicManager -> AddressBookParser : parseCommand("/delete ; name : Poochie") activate AddressBookParser create DeleteCommandParser @@ -27,7 +27,7 @@ activate DeleteCommandParser DeleteCommandParser --> AddressBookParser deactivate DeleteCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") +AddressBookParser -> DeleteCommandParser : parse("Poochie") activate DeleteCommandParser create DeleteCommand @@ -46,10 +46,10 @@ destroy DeleteCommandParser AddressBookParser --> LogicManager : d deactivate AddressBookParser -LogicManager -> DeleteCommand : execute(m) +LogicManager -> DeleteCommand : execute(...) activate DeleteCommand -DeleteCommand -> Model : deletePerson(1) +DeleteCommand -> Model : deletePerson(Poochie) activate Model Model --> DeleteCommand diff --git a/docs/diagrams/EditCommandSequenceDiagram.puml b/docs/diagrams/EditCommandSequenceDiagram.puml new file mode 100644 index 00000000000..f425269105c --- /dev/null +++ b/docs/diagrams/EditCommandSequenceDiagram.puml @@ -0,0 +1,84 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":EditCommandParser" as EditCommandParser LOGIC_COLOR +participant "r:EditCommand" as EditCommand LOGIC_COLOR +participant "r:EditPersonDescriptor" as EditPersonDescriptor LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("/edit-person ; name : Alice ...") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("/edit-person ; name : Alice ...") +activate AddressBookParser + +create EditCommandParser +AddressBookParser -> EditCommandParser +activate EditCommandParser + +EditCommandParser --> AddressBookParser +deactivate EditCommandParser + +AddressBookParser -> EditCommandParser : parse(" ; name : Alice ...") +activate EditCommandParser + +create EditPersonDescriptor +EditCommandParser -> EditPersonDescriptor +activate EditPersonDescriptor +EditPersonDescriptor -> EditCommandParser +deactivate EditPersonDescriptor + +create EditCommand +EditCommandParser -> EditCommand +activate EditCommand + +EditCommand --> EditCommandParser : r +deactivate EditCommand + +EditCommandParser --> AddressBookParser : r +deactivate EditCommandParser + +EditCommandParser -[hidden]-> AddressBookParser +destroy EditCommandParser + +AddressBookParser --> LogicManager : r +deactivate AddressBookParser + +LogicManager -> EditCommand : execute(...) +activate EditCommand + +EditCommand -> Model : findByName(name) +activate Model + +Model --> EditCommand + +EditCommand -> Model : setPerson(personToEdit, editedPerson) + +Model --> EditCommand + +EditCommand -> Model : updateFilteredPersonListWithCommit() + +Model --> EditCommand +deactivate Model + +create CommandResult +EditCommand -> CommandResult +activate CommandResult + +CommandResult --> EditCommand +deactivate CommandResult + +EditCommand --> LogicManager : result +deactivate EditCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/HelpCommandSequenceDiagram.puml b/docs/diagrams/HelpCommandSequenceDiagram.puml new file mode 100644 index 00000000000..70e03dbaa8c --- /dev/null +++ b/docs/diagrams/HelpCommandSequenceDiagram.puml @@ -0,0 +1,64 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":HelpCommandParser" as HelpCommandParser LOGIC_COLOR +participant "r:HelpCommand" as HelpCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + + +[-> LogicManager : execute("/help ; command : delete") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand(" ; command : delete") +activate AddressBookParser + +create HelpCommandParser +AddressBookParser -> HelpCommandParser +activate HelpCommandParser + +HelpCommandParser --> AddressBookParser +deactivate HelpCommandParser + +AddressBookParser -> HelpCommandParser : parse(" ; command : delete") +activate HelpCommandParser + +create HelpCommand +HelpCommandParser -> HelpCommand +activate HelpCommand + +HelpCommand --> HelpCommandParser : r +deactivate HelpCommand + +HelpCommandParser --> AddressBookParser : r +deactivate HelpCommandParser + +HelpCommandParser -[hidden]-> AddressBookParser +destroy HelpCommandParser + +AddressBookParser --> LogicManager : r +deactivate AddressBookParser + +LogicManager -> HelpCommand : execute(...) +activate HelpCommand + + + + + +create CommandResult +HelpCommand -> CommandResult +activate CommandResult + +CommandResult --> HelpCommand +deactivate CommandResult +HelpCommand --> LogicManager + +deactivate HelpCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ListCommandSequenceDiagram.puml b/docs/diagrams/ListCommandSequenceDiagram.puml new file mode 100644 index 00000000000..28fdb8ee180 --- /dev/null +++ b/docs/diagrams/ListCommandSequenceDiagram.puml @@ -0,0 +1,40 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant "r:ListCommand" as ListCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute("/list") +activate LogicManager + + + +LogicManager -> ListCommand : execute(...) +activate ListCommand + + +ListCommand -> Model : updateFilteredPersonList() +activate Model + +Model --> ListCommand +deactivate Model + +create CommandResult +ListCommand -> CommandResult +activate CommandResult + +CommandResult --> ListCommand +deactivate CommandResult + +ListCommand --> LogicManager : result +deactivate ListCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index 58b4f602ce6..ae2b39fa710 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -39,7 +39,7 @@ LogicManager --> Storage Storage --[hidden] Model Command .[hidden]up.> Storage Command .right.> Model -note right of XYZCommand: XYZCommand = AddCommand, \nFindCommand, etc +note right of XYZCommand: XYZCommand = AddStaffCommand, \nSearchCommand, etc Logic ..> CommandResult LogicManager .down.> CommandResult diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 0de5673070d..5d1f6641876 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -10,15 +10,30 @@ Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs Class "<>\nModel" as Model Class AddressBook Class ModelManager +Class VersionedAddressBook Class UserPrefs +Class addressBookStateList +Class currentStatePointer Class UniquePersonList Class Person -Class Address -Class Email +Class Staff +Class Supplier +Class Maintainer Class Name Class Phone +Class Email +Class Address +Class Salary +Class Employment +Class Price +Class Product +Class Skill +Class Commission Class Tag +Class Pin +Class Note +Class Rating Class I #FFFFFF } @@ -31,17 +46,36 @@ AddressBook .up.|> ReadOnlyAddressBook ModelManager .up.|> Model Model .right.> ReadOnlyUserPrefs Model .left.> ReadOnlyAddressBook -ModelManager -left-> "1" AddressBook +ModelManager -left-> "1" VersionedAddressBook +VersionedAddressBook -left-|> AddressBook +VersionedAddressBook *--> "1" addressBookStateList +VersionedAddressBook *--> "1" currentStatePointer ModelManager -right-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs AddressBook *--> "1" UniquePersonList UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag +Person *--> "1" Name +Person *--> "1" Phone +Person *--> "1" Email +Person *--> "1" Address +Person *--> "1" Tag +Person *--> "1" Pin +Person *--> "1" Note +Person *--> "1" Rating + +Person <|-- Staff +Person <|-- Supplier +Person <|-- Maintainer + +Staff *--> "1"Salary +Staff *--> "1"Employment + +Supplier *--> "1"Price +Supplier *--> "1"Product + +Maintainer *--> "1"Skill +Maintainer *--> "1"Commission Person -[hidden]up--> I UniquePersonList -[hidden]right-> I diff --git a/docs/diagrams/NoteCommandSequenceDiagram.puml b/docs/diagrams/NoteCommandSequenceDiagram.puml new file mode 100644 index 00000000000..91eb42e2fd7 --- /dev/null +++ b/docs/diagrams/NoteCommandSequenceDiagram.puml @@ -0,0 +1,79 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":NoteCommandParser" as NoteCommandParser LOGIC_COLOR +participant "r:NoteCommand" as NoteCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute("/note ; name : poochie ...") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("/note ; name : poochie...") +activate AddressBookParser + +create NoteCommandParser +AddressBookParser -> NoteCommandParser +activate NoteCommandParser + +NoteCommandParser --> AddressBookParser +deactivate NoteCommandParser + +AddressBookParser -> NoteCommandParser : parse(" ; name : poochie ...") +activate NoteCommandParser + +create NoteCommand +NoteCommandParser -> NoteCommand +activate NoteCommand + +NoteCommand --> NoteCommandParser : r +deactivate NoteCommand + +NoteCommandParser --> AddressBookParser : r +deactivate NoteCommandParser + +NoteCommandParser -[hidden]-> AddressBookParser +destroy NoteCommandParser + +AddressBookParser --> LogicManager : r +deactivate AddressBookParser + +LogicManager -> NoteCommand : execute(...) +activate NoteCommand + +NoteCommand -> Model : findByName(name) +activate Model +Model --> NoteCommand + +NoteCommand -> Model : setPerson(personToEdit, editedPerson) +Model --> NoteCommand + +NoteCommand -> Model : findByName(name) +activate Model + +Model --> NoteCommand + +NoteCommand -> Model : updateFilteredPersonListWithCommit() + +Model --> NoteCommand +deactivate Model + +create CommandResult +NoteCommand -> CommandResult +activate CommandResult + +CommandResult --> NoteCommand +deactivate CommandResult + +NoteCommand --> LogicManager : result +deactivate NoteCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/PinCommandSequenceDiagram.puml b/docs/diagrams/PinCommandSequenceDiagram.puml new file mode 100644 index 00000000000..0ce6bfef148 --- /dev/null +++ b/docs/diagrams/PinCommandSequenceDiagram.puml @@ -0,0 +1,76 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":PinCommandParser" as PinCommandParser LOGIC_COLOR +participant "r:PinCommand" as PinCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute("/pin ; name : poochie") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("/pin ; name : poochie") +activate AddressBookParser + +create PinCommandParser +AddressBookParser -> PinCommandParser +activate PinCommandParser + +PinCommandParser --> AddressBookParser +deactivate PinCommandParser + +AddressBookParser -> PinCommandParser : parse(" ; name : poochie") +activate PinCommandParser + +create PinCommand +PinCommandParser -> PinCommand +activate PinCommand + +PinCommand --> PinCommandParser : r +deactivate PinCommand + +PinCommandParser --> AddressBookParser : r +deactivate PinCommandParser + +PinCommandParser -[hidden]-> AddressBookParser +destroy PinCommandParser + +AddressBookParser --> LogicManager : r +deactivate AddressBookParser + +LogicManager -> PinCommand : execute(...) +activate PinCommand + +PinCommand -> Model : findByName(name) +activate Model + +Model --> PinCommand + +PinCommand -> Model : setPerson(personToPin, pinnedPerson) + +Model --> PinCommand + +PinCommand -> Model : updatePinnedPersonList() + +Model --> PinCommand +deactivate Model + +create CommandResult +PinCommand -> CommandResult +activate CommandResult + +CommandResult --> PinCommand +deactivate CommandResult + +PinCommand --> LogicManager : result +deactivate PinCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/RateCommandSequenceDiagram.puml b/docs/diagrams/RateCommandSequenceDiagram.puml new file mode 100644 index 00000000000..dcb27744f9f --- /dev/null +++ b/docs/diagrams/RateCommandSequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":RateCommandParser" as RateCommandParser LOGIC_COLOR +participant "r:RateCommand" as RateCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute("/rate ; name : Poochie ; rating : 5") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("/rate ; name : Poochie ; rating : 5") +activate AddressBookParser + +create RateCommandParser +AddressBookParser -> RateCommandParser +activate RateCommandParser + +RateCommandParser --> AddressBookParser +deactivate RateCommandParser + +AddressBookParser -> RateCommandParser : parse(" ; name : Poochie ; rating : 5") +activate RateCommandParser + +create RateCommand +RateCommandParser -> RateCommand +activate RateCommand + +RateCommand --> RateCommandParser : r +deactivate RateCommand + +RateCommandParser --> AddressBookParser : r +deactivate RateCommandParser + +RateCommandParser -[hidden]-> AddressBookParser +destroy RateCommandParser + +AddressBookParser --> LogicManager : r +deactivate AddressBookParser + +LogicManager -> RateCommand : execute(...) +activate RateCommand + +RateCommand -> Model : findByName(name) +activate Model + +RateCommand -> Model : setPerson(personToEdit, editedPerson) +activate Model + +Model --> RateCommand + +RateCommand -> Model : updateFilteredPersonListWithCommit() + +Model --> RateCommand +deactivate Model + +create CommandResult +RateCommand -> CommandResult +activate CommandResult + +CommandResult --> RateCommand +deactivate CommandResult + +RateCommand --> LogicManager : result +deactivate RateCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/RemindCommandSequenceDiagram.puml b/docs/diagrams/RemindCommandSequenceDiagram.puml new file mode 100644 index 00000000000..0cb2c0f6800 --- /dev/null +++ b/docs/diagrams/RemindCommandSequenceDiagram.puml @@ -0,0 +1,40 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant "r:RemindCommand" as RemindCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute("/remind") +activate LogicManager + + + +LogicManager -> RemindCommand : execute(...) +activate RemindCommand + + +RemindCommand -> Model : updateFilteredPersonList() +activate Model + +Model --> RemindCommand +deactivate Model + +create CommandResult +RemindCommand -> CommandResult +activate CommandResult + +CommandResult --> RemindCommand +deactivate CommandResult + +RemindCommand --> LogicManager : result +deactivate RemindCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/SearchCommandSequenceDiagram.puml b/docs/diagrams/SearchCommandSequenceDiagram.puml new file mode 100644 index 00000000000..d1bd8762e3b --- /dev/null +++ b/docs/diagrams/SearchCommandSequenceDiagram.puml @@ -0,0 +1,68 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":SearchCommandParser" as SearchCommandParser LOGIC_COLOR +participant "r:SearchCommand" as SearchCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute("/search ; phone : 98765432") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("/search ; phone : 98765432") +activate AddressBookParser + +create SearchCommandParser +AddressBookParser -> SearchCommandParser +activate SearchCommandParser + +SearchCommandParser --> AddressBookParser +deactivate SearchCommandParser + +AddressBookParser -> SearchCommandParser : parse(" ; phone : 98765432") +activate SearchCommandParser + +create SearchCommand +SearchCommandParser -> SearchCommand +activate SearchCommand + +SearchCommand --> SearchCommandParser : r +deactivate SearchCommand + +SearchCommandParser --> AddressBookParser : r +deactivate SearchCommandParser + +SearchCommandParser -[hidden]-> AddressBookParser +destroy SearchCommandParser + +AddressBookParser --> LogicManager : r +deactivate AddressBookParser + +LogicManager -> SearchCommand : execute(...) +activate SearchCommand + +SearchCommand -> Model : updateFilteredPersonList(KeywordPredicate) +activate Model + +Model --> SearchCommand +deactivate Model + +create CommandResult +SearchCommand -> CommandResult +activate CommandResult + +CommandResult --> SearchCommand +deactivate CommandResult + +SearchCommand --> LogicManager : result +deactivate SearchCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/SortCommandSequenceDiagram.puml b/docs/diagrams/SortCommandSequenceDiagram.puml new file mode 100644 index 00000000000..21b47fdb23a --- /dev/null +++ b/docs/diagrams/SortCommandSequenceDiagram.puml @@ -0,0 +1,68 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":SortCommandParser" as SortCommandParser LOGIC_COLOR +participant "r:SortCommand" as SortCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute("/sort ; field : phone") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("/sort ; field : phone") +activate AddressBookParser + +create SortCommandParser +AddressBookParser -> SortCommandParser +activate SortCommandParser + +SortCommandParser --> AddressBookParser +deactivate SortCommandParser + +AddressBookParser -> SortCommandParser : parse(" ; field : phone") +activate SortCommandParser + +create SortCommand +SortCommandParser -> SortCommand +activate SortCommand + +SortCommand --> SortCommandParser : r +deactivate SortCommand + +SortCommandParser --> AddressBookParser : r +deactivate SortCommandParser + +SortCommandParser -[hidden]-> AddressBookParser +destroy SortCommandParser + +AddressBookParser --> LogicManager : r +deactivate AddressBookParser + +LogicManager -> SortCommand : execute(...) +activate SortCommand + +SortCommand -> Model : updateSortedPersonList(phone) +activate Model + +Model --> SortCommand +deactivate Model + +create CommandResult +SortCommand -> CommandResult +activate CommandResult + +CommandResult --> SortCommand +deactivate CommandResult + +SortCommand --> LogicManager : result +deactivate SortCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index a821e06458c..fb11d5d7463 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -19,6 +19,7 @@ Class "<>\nAddressBookStorage" as AddressBookStorage Class JsonAddressBookStorage Class JsonSerializableAddressBook Class JsonAdaptedPerson +class JsonAdaptedXYZ Class JsonAdaptedTag } @@ -38,6 +39,7 @@ JsonUserPrefsStorage .up.|> UserPrefsStorage JsonAddressBookStorage .up.|> AddressBookStorage JsonAddressBookStorage ..> JsonSerializableAddressBook JsonSerializableAddressBook --> "*" JsonAdaptedPerson -JsonAdaptedPerson --> "*" JsonAdaptedTag +JsonAdaptedPerson -right-> "*" JsonAdaptedTag +JsonAdaptedPerson <|-down- JsonAdaptedXYZ @enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 95473d5aa19..4f7b7c37c23 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -34,7 +34,7 @@ MainWindow *-down-> "1" CommandBox MainWindow *-down-> "1" ResultDisplay MainWindow *-down-> "1" PersonListPanel MainWindow *-down-> "1" StatusBarFooter -MainWindow --> "0..1" HelpWindow +MainWindow *--> "1" HelpWindow PersonListPanel -down-> "*" PersonCard diff --git a/docs/diagrams/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml index 5a41e9e1651..7fa2d0a40a1 100644 --- a/docs/diagrams/UndoRedoState1.puml +++ b/docs/diagrams/UndoRedoState1.puml @@ -4,7 +4,7 @@ skinparam ClassFontColor #000000 skinparam ClassBorderColor #000000 skinparam ClassBackgroundColor #FFFFAA -title After command "delete 5" +title After command "/delete ; name : Poochie" package States <> { class State1 as "ab0:AddressBook" diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml index ad32fce1b0b..71345b2c93c 100644 --- a/docs/diagrams/UndoRedoState2.puml +++ b/docs/diagrams/UndoRedoState2.puml @@ -4,7 +4,7 @@ skinparam ClassFontColor #000000 skinparam ClassBorderColor #000000 skinparam ClassBackgroundColor #FFFFAA -title After command "add n/David" +title After command "/add-person ; name : John ......" package States <> { class State1 as "ab0:AddressBook" diff --git a/docs/diagrams/UndoRedoState3.puml b/docs/diagrams/UndoRedoState3.puml index 9187a690036..83b9c138219 100644 --- a/docs/diagrams/UndoRedoState3.puml +++ b/docs/diagrams/UndoRedoState3.puml @@ -4,7 +4,7 @@ skinparam ClassFontColor #000000 skinparam ClassBorderColor #000000 skinparam ClassBackgroundColor #FFFFAA -title After command "undo" +title After command "/undo" package States <> { class State1 as "ab0:AddressBook" diff --git a/docs/diagrams/UndoRedoState4.puml b/docs/diagrams/UndoRedoState4.puml index 2bc631ffcd0..4f8211f4953 100644 --- a/docs/diagrams/UndoRedoState4.puml +++ b/docs/diagrams/UndoRedoState4.puml @@ -4,7 +4,7 @@ skinparam ClassFontColor #000000 skinparam ClassBorderColor #000000 skinparam ClassBackgroundColor #FFFFAA -title After command "list" +title After command "/list" package States <> { class State1 as "ab0:AddressBook" diff --git a/docs/diagrams/UndoRedoState5.puml b/docs/diagrams/UndoRedoState5.puml index e77b04104aa..c26789e2197 100644 --- a/docs/diagrams/UndoRedoState5.puml +++ b/docs/diagrams/UndoRedoState5.puml @@ -4,7 +4,7 @@ skinparam ClassFontColor #000000 skinparam ClassBorderColor #000000 skinparam ClassBackgroundColor #FFFFAA -title After command "clear" +title After command "/clear" package States <> { class State1 as "ab0:AddressBook" diff --git a/docs/diagrams/UndoSequenceDiagram-Logic.puml b/docs/diagrams/UndoSequenceDiagram-Logic.puml index e57368c5159..241f0f4e0dc 100644 --- a/docs/diagrams/UndoSequenceDiagram-Logic.puml +++ b/docs/diagrams/UndoSequenceDiagram-Logic.puml @@ -11,10 +11,10 @@ end box box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR end box -[-> LogicManager : execute(undo) +[-> LogicManager : execute("/undo") activate LogicManager -LogicManager -> AddressBookParser : parseCommand(undo) +LogicManager -> AddressBookParser : parseCommand("/undo") activate AddressBookParser create UndoCommand @@ -27,7 +27,7 @@ deactivate UndoCommand AddressBookParser --> LogicManager : u deactivate AddressBookParser -LogicManager -> UndoCommand : execute() +LogicManager -> UndoCommand : execute(...) activate UndoCommand UndoCommand -> Model : undoAddressBook() diff --git a/docs/diagrams/UnpinCommandSequenceDiagram.puml b/docs/diagrams/UnpinCommandSequenceDiagram.puml new file mode 100644 index 00000000000..d2b68e3b2e5 --- /dev/null +++ b/docs/diagrams/UnpinCommandSequenceDiagram.puml @@ -0,0 +1,76 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":UnpinCommandParser" as UnpinCommandParser LOGIC_COLOR +participant "r:UnpinCommand" as UnpinCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box +[-> LogicManager : execute("/unpin ; name : poochie") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("/unpin ; name : poochie") +activate AddressBookParser + +create UnpinCommandParser +AddressBookParser -> UnpinCommandParser +activate UnpinCommandParser + +UnpinCommandParser --> AddressBookParser +deactivate UnpinCommandParser + +AddressBookParser -> UnpinCommandParser : parse(" ; name : poochie") +activate UnpinCommandParser + +create UnpinCommand +UnpinCommandParser -> UnpinCommand +activate UnpinCommand + +UnpinCommand --> UnpinCommandParser : r +deactivate UnpinCommand + +UnpinCommandParser --> AddressBookParser : r +deactivate UnpinCommandParser + +UnpinCommandParser -[hidden]-> AddressBookParser +destroy UnpinCommandParser + +AddressBookParser --> LogicManager : r +deactivate AddressBookParser + +LogicManager -> UnpinCommand : execute(...) +activate UnpinCommand + +UnpinCommand -> Model : findByName(name) +activate Model + +Model --> UnpinCommand + +UnpinCommand -> Model : setPerson(personToUnpin, unpinnedPerson) + +Model --> UnpinCommand + +UnpinCommand -> Model : updatePinnedPersonList() + +Model --> UnpinCommand +deactivate Model + +create CommandResult +UnpinCommand -> CommandResult +activate CommandResult + +CommandResult --> UnpinCommand +deactivate CommandResult + +UnpinCommand --> LogicManager : result +deactivate UnpinCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/style.puml b/docs/diagrams/style.puml index f7d7347ae84..c6170e31a1c 100644 --- a/docs/diagrams/style.puml +++ b/docs/diagrams/style.puml @@ -25,6 +25,8 @@ !define MODEL_COLOR_T3 #7B000E !define MODEL_COLOR_T4 #51000A +!define BOOK_COLOR #800080 + !define STORAGE_COLOR #A38300 !define STORAGE_COLOR_T1 #FFE374 !define STORAGE_COLOR_T2 #EDC520 diff --git a/docs/images/AddCommandSequenceDiagram.png b/docs/images/AddCommandSequenceDiagram.png new file mode 100644 index 00000000000..4d9547263e7 Binary files /dev/null and b/docs/images/AddCommandSequenceDiagram.png differ diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 37ad06a2803..dadade0d458 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/ClearCommandSequenceDiagram.png b/docs/images/ClearCommandSequenceDiagram.png new file mode 100644 index 00000000000..9ad8a134ceb Binary files /dev/null and b/docs/images/ClearCommandSequenceDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index ac2ae217c51..589f37e3d71 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/EditCommandSequenceDiagram.png b/docs/images/EditCommandSequenceDiagram.png new file mode 100644 index 00000000000..4e363f60a8d Binary files /dev/null and b/docs/images/EditCommandSequenceDiagram.png differ diff --git a/docs/images/HelpCommandSequenceDiagram.png b/docs/images/HelpCommandSequenceDiagram.png new file mode 100644 index 00000000000..5b4b2744f74 Binary files /dev/null and b/docs/images/HelpCommandSequenceDiagram.png differ diff --git a/docs/images/ListCommandSequenceDiagram.png b/docs/images/ListCommandSequenceDiagram.png new file mode 100644 index 00000000000..9cebe95fe1c Binary files /dev/null and b/docs/images/ListCommandSequenceDiagram.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index fe91c69efe7..e4c771c31fc 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index a19fb1b4ac8..f12b2e2b8d0 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/NoteCommandSequenceDiagram.png b/docs/images/NoteCommandSequenceDiagram.png new file mode 100644 index 00000000000..0e894dc776f Binary files /dev/null and b/docs/images/NoteCommandSequenceDiagram.png differ diff --git a/docs/images/PinCommandSequenceDiagram.png b/docs/images/PinCommandSequenceDiagram.png new file mode 100644 index 00000000000..c84123fff18 Binary files /dev/null and b/docs/images/PinCommandSequenceDiagram.png differ diff --git a/docs/images/PoochPlannerLogo.png b/docs/images/PoochPlannerLogo.png new file mode 100644 index 00000000000..5f7f75fc997 Binary files /dev/null and b/docs/images/PoochPlannerLogo.png differ diff --git a/docs/images/RateCommandSequenceDiagram.png b/docs/images/RateCommandSequenceDiagram.png new file mode 100644 index 00000000000..961142acc59 Binary files /dev/null and b/docs/images/RateCommandSequenceDiagram.png differ diff --git a/docs/images/RemindCommandSequenceDiagram.png b/docs/images/RemindCommandSequenceDiagram.png new file mode 100644 index 00000000000..2931af93b63 Binary files /dev/null and b/docs/images/RemindCommandSequenceDiagram.png differ diff --git a/docs/images/SearchCommandSequenceDiagram.png b/docs/images/SearchCommandSequenceDiagram.png new file mode 100644 index 00000000000..7b7e7a35aaf Binary files /dev/null and b/docs/images/SearchCommandSequenceDiagram.png differ diff --git a/docs/images/SortCommandSequenceDiagram.png b/docs/images/SortCommandSequenceDiagram.png new file mode 100644 index 00000000000..174b2d29fa5 Binary files /dev/null and b/docs/images/SortCommandSequenceDiagram.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 18fa4d0d51f..0c2813521c9 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5bd77847aa2..96ef4a4c800 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 11f06d68671..cd1c82a7715 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/UndoRedoState1.png b/docs/images/UndoRedoState1.png index 2d3ad09c047..e67babcc50d 100644 Binary files a/docs/images/UndoRedoState1.png and b/docs/images/UndoRedoState1.png differ diff --git a/docs/images/UndoRedoState2.png b/docs/images/UndoRedoState2.png index 20853694e03..2139fef325c 100644 Binary files a/docs/images/UndoRedoState2.png and b/docs/images/UndoRedoState2.png differ diff --git a/docs/images/UndoRedoState3.png b/docs/images/UndoRedoState3.png index 1a9551b31be..1e36ba0b6af 100644 Binary files a/docs/images/UndoRedoState3.png and b/docs/images/UndoRedoState3.png differ diff --git a/docs/images/UndoRedoState4.png b/docs/images/UndoRedoState4.png index 46dfae78c94..9cab52153e9 100644 Binary files a/docs/images/UndoRedoState4.png and b/docs/images/UndoRedoState4.png differ diff --git a/docs/images/UndoRedoState5.png b/docs/images/UndoRedoState5.png index f45889b5fdf..fcf089a0b99 100644 Binary files a/docs/images/UndoRedoState5.png and b/docs/images/UndoRedoState5.png differ diff --git a/docs/images/UndoSequenceDiagram-Logic.png b/docs/images/UndoSequenceDiagram-Logic.png index 78e95214294..695eeb49d2a 100644 Binary files a/docs/images/UndoSequenceDiagram-Logic.png and b/docs/images/UndoSequenceDiagram-Logic.png differ diff --git a/docs/images/UnpinCommandSequenceDiagram.png b/docs/images/UnpinCommandSequenceDiagram.png new file mode 100644 index 00000000000..869a8da58e1 Binary files /dev/null and b/docs/images/UnpinCommandSequenceDiagram.png differ diff --git a/docs/images/chiageng.png b/docs/images/chiageng.png new file mode 100644 index 00000000000..ff8be813332 Binary files /dev/null and b/docs/images/chiageng.png differ diff --git a/docs/images/dg-images/search-command-sequence-diagram.png b/docs/images/dg-images/search-command-sequence-diagram.png new file mode 100644 index 00000000000..c9479f44b82 Binary files /dev/null and b/docs/images/dg-images/search-command-sequence-diagram.png differ diff --git a/docs/images/dg-images/sort-command-sequence-diagram.png b/docs/images/dg-images/sort-command-sequence-diagram.png new file mode 100644 index 00000000000..52f6eb7f9c1 Binary files /dev/null and b/docs/images/dg-images/sort-command-sequence-diagram.png differ diff --git a/docs/images/jamessinmaojun.png b/docs/images/jamessinmaojun.png new file mode 100644 index 00000000000..95f048a69d8 Binary files /dev/null and b/docs/images/jamessinmaojun.png differ diff --git a/docs/images/jannaleong.png b/docs/images/jannaleong.png new file mode 100644 index 00000000000..3ece69c7168 Binary files /dev/null and b/docs/images/jannaleong.png differ diff --git a/docs/images/joshy837.png b/docs/images/joshy837.png new file mode 100644 index 00000000000..a8fa8d1a69f Binary files /dev/null and b/docs/images/joshy837.png differ diff --git a/docs/images/ug-images/BasicGuiNavigation.png b/docs/images/ug-images/BasicGuiNavigation.png new file mode 100644 index 00000000000..f93e1302922 Binary files /dev/null and b/docs/images/ug-images/BasicGuiNavigation.png differ diff --git a/docs/images/ug-images/PersonCardGuiNavigation.png b/docs/images/ug-images/PersonCardGuiNavigation.png new file mode 100644 index 00000000000..051a8757503 Binary files /dev/null and b/docs/images/ug-images/PersonCardGuiNavigation.png differ diff --git a/docs/images/ug-images/command-images/addcommand.png b/docs/images/ug-images/command-images/addcommand.png new file mode 100644 index 00000000000..0be066a7dc3 Binary files /dev/null and b/docs/images/ug-images/command-images/addcommand.png differ diff --git a/docs/images/ug-images/command-images/beforeundocommand.png b/docs/images/ug-images/command-images/beforeundocommand.png new file mode 100644 index 00000000000..06c5ce94916 Binary files /dev/null and b/docs/images/ug-images/command-images/beforeundocommand.png differ diff --git a/docs/images/ug-images/command-images/clearcommand.png b/docs/images/ug-images/command-images/clearcommand.png new file mode 100644 index 00000000000..bc39f76d8b0 Binary files /dev/null and b/docs/images/ug-images/command-images/clearcommand.png differ diff --git a/docs/images/ug-images/command-images/deadlinecommand.png b/docs/images/ug-images/command-images/deadlinecommand.png new file mode 100644 index 00000000000..32f3096422d Binary files /dev/null and b/docs/images/ug-images/command-images/deadlinecommand.png differ diff --git a/docs/images/ug-images/command-images/deletecommand.png b/docs/images/ug-images/command-images/deletecommand.png new file mode 100644 index 00000000000..5bbeb86ab12 Binary files /dev/null and b/docs/images/ug-images/command-images/deletecommand.png differ diff --git a/docs/images/ug-images/command-images/editcommand.png b/docs/images/ug-images/command-images/editcommand.png new file mode 100644 index 00000000000..c16eb4a823b Binary files /dev/null and b/docs/images/ug-images/command-images/editcommand.png differ diff --git a/docs/images/ug-images/command-images/helpcommand.png b/docs/images/ug-images/command-images/helpcommand.png new file mode 100644 index 00000000000..6befd1d0c4c Binary files /dev/null and b/docs/images/ug-images/command-images/helpcommand.png differ diff --git a/docs/images/ug-images/command-images/listcommand.png b/docs/images/ug-images/command-images/listcommand.png new file mode 100644 index 00000000000..79eb7012b8c Binary files /dev/null and b/docs/images/ug-images/command-images/listcommand.png differ diff --git a/docs/images/ug-images/command-images/notecommand.png b/docs/images/ug-images/command-images/notecommand.png new file mode 100644 index 00000000000..52d8d12c5d8 Binary files /dev/null and b/docs/images/ug-images/command-images/notecommand.png differ diff --git a/docs/images/ug-images/command-images/pincommand.png b/docs/images/ug-images/command-images/pincommand.png new file mode 100644 index 00000000000..1ac8aafe4ef Binary files /dev/null and b/docs/images/ug-images/command-images/pincommand.png differ diff --git a/docs/images/ug-images/command-images/ratecommand.png b/docs/images/ug-images/command-images/ratecommand.png new file mode 100644 index 00000000000..3d91e8f3151 Binary files /dev/null and b/docs/images/ug-images/command-images/ratecommand.png differ diff --git a/docs/images/ug-images/command-images/redocommand.png b/docs/images/ug-images/command-images/redocommand.png new file mode 100644 index 00000000000..85c12e33a75 Binary files /dev/null and b/docs/images/ug-images/command-images/redocommand.png differ diff --git a/docs/images/ug-images/command-images/remindcommand.png b/docs/images/ug-images/command-images/remindcommand.png new file mode 100644 index 00000000000..8cc5a6fc37e Binary files /dev/null and b/docs/images/ug-images/command-images/remindcommand.png differ diff --git a/docs/images/ug-images/command-images/searchcommand.png b/docs/images/ug-images/command-images/searchcommand.png new file mode 100644 index 00000000000..743138df097 Binary files /dev/null and b/docs/images/ug-images/command-images/searchcommand.png differ diff --git a/docs/images/ug-images/command-images/sortcommand.png b/docs/images/ug-images/command-images/sortcommand.png new file mode 100644 index 00000000000..98ba205055e Binary files /dev/null and b/docs/images/ug-images/command-images/sortcommand.png differ diff --git a/docs/images/ug-images/command-images/undocommand.png b/docs/images/ug-images/command-images/undocommand.png new file mode 100644 index 00000000000..9b720148f96 Binary files /dev/null and b/docs/images/ug-images/command-images/undocommand.png differ diff --git a/docs/images/ug-images/command-images/unpincommand.png b/docs/images/ug-images/command-images/unpincommand.png new file mode 100644 index 00000000000..693db229641 Binary files /dev/null and b/docs/images/ug-images/command-images/unpincommand.png differ diff --git a/docs/images/ug-images/error-images/incorrect-phone-format.png b/docs/images/ug-images/error-images/incorrect-phone-format.png new file mode 100644 index 00000000000..ef711f0b3b0 Binary files /dev/null and b/docs/images/ug-images/error-images/incorrect-phone-format.png differ diff --git a/docs/images/ug-images/error-images/incorrect-salary-format.png b/docs/images/ug-images/error-images/incorrect-salary-format.png new file mode 100644 index 00000000000..e3d8d0c8bfa Binary files /dev/null and b/docs/images/ug-images/error-images/incorrect-salary-format.png differ diff --git a/docs/images/ug-images/error-images/invalid-field.png b/docs/images/ug-images/error-images/invalid-field.png new file mode 100644 index 00000000000..951449ee81e Binary files /dev/null and b/docs/images/ug-images/error-images/invalid-field.png differ diff --git a/docs/images/ug-images/error-images/invalidField.png b/docs/images/ug-images/error-images/invalidField.png new file mode 100644 index 00000000000..76a78d78017 Binary files /dev/null and b/docs/images/ug-images/error-images/invalidField.png differ diff --git a/docs/images/ug-images/error-images/missingField.png b/docs/images/ug-images/error-images/missingField.png new file mode 100644 index 00000000000..905c4971e43 Binary files /dev/null and b/docs/images/ug-images/error-images/missingField.png differ diff --git a/docs/images/ug-images/error-images/unknownCommand.png b/docs/images/ug-images/error-images/unknownCommand.png new file mode 100644 index 00000000000..adc61eb6a1c Binary files /dev/null and b/docs/images/ug-images/error-images/unknownCommand.png differ diff --git a/docs/images/yleeyilin.png b/docs/images/yleeyilin.png new file mode 100644 index 00000000000..ca6e6b4447d Binary files /dev/null and b/docs/images/yleeyilin.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..c12f2ca866a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ --- layout: page -title: AddressBook Level-3 +title: PoochPlanner --- [![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) @@ -8,10 +8,10 @@ title: AddressBook Level-3 ![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). +**PoochPlanner is a desktop application crafted specially for dog cafe owners. PoochPlanner track details of various contact types (general contact, supplier, staff, maintainer) that dog cafe owners have to regularly interact with.** The app is optimised for use using Command Line Interface (CLI) while still encompassing a user-friend Graphical User Interface (GUI). -* 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 PoochPlanner, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). +* If you are interested in working with us to develop PoochPlanner, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** diff --git a/docs/team/chiageng.md b/docs/team/chiageng.md new file mode 100644 index 00000000000..4c984ecae14 --- /dev/null +++ b/docs/team/chiageng.md @@ -0,0 +1,65 @@ +--- +layout: page +title: Chia Geng's Project Portfolio Page +--- + +### Project: PoochPlanner + +PoochPlanner is a desktop application to track details of various groups (supplier, maintainer, staff) that dog cafe owners have to regularly interact with. The app is optimised for use using Command Line Interface (CLI) while still encompassing a user-friendly Graphical User Interface (GUI). + +Given below are my contributions to the project. + +* **New Feature : Add**: Added the ability to add 4 different type of contacts. (Pull requests [\#57](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/57)) + * What it does: allows the user to add a new contact. + * Justification: This feature improves the product significantly because a user can store all contacts in this address book. + * 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. + +* **Storing Json** : Handled the serialization of different type of contacts to store as JSON objects. (Pull requests [\#57](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/57)) + * What it does: store different type of contact into JSON object. + * Justification: This feature improves the product significantly because a user can store all contacts into local storage. + * Highlights: This enhancement affects existing project structure. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing project structure. + +* **Reading Json** : Handled the serialization of reading different type of contacts from JSON file. (Pull requests [\#57](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/57)) + * What it does: read different type of contact from JSON file. + * Justification: This feature improves the product significantly because a user can retrieve all contacts from local storage. + * Highlights: This enhancement affects existing project structure. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing project structure. + +* **New Feature : Undo** : Added ability to undo unlimited times for any wrong executed commands that modified address book. (Pull requests [\#114](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/114)) + * What it does: Undo a wrong executed commnad that modified addressbook. + * Justification: This feature improves the product significantly because a user can retrieve history if wrong command is executed accidentally. + * Highlights: This enhancement affects existing project structure. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing project structure. + +* **New Feature : Redo** : Added ability to redo unlimited times for any wrong executed undo commands. (Pull requests [\#114](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/114)) + * What it does: Redo a wrong executed undo commnad. + * Justification: This feature improves the product significantly because a user can retrieve history if wrong undo command is executed accidentally. + * Highlights: This enhancement affects existing project structure. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing project structure. + +* **Exception Handling Hierarchy** : Standardise error handling hierarchy for every command. (Pull requests [\#173](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/173)) + * What it does: Standardise the flow of error handling to ensure that bug is easier to be discovered in future. + * Justification: This standardisation will allow developers to work in a more systematic manner to avoid messy code. + * Highlights: This enhancement requires a deep understanding on current code in order to create a dynamic exception handling functions which is usable for every command. This was challenging to ensure the abstraction is done elegantly. + + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2024-02-23&tabOpen=true&tabType=authorship&tabAuthor=chiageng&tabRepo=AY2324S2-CS2103T-W10-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + +* **Enhancements to existing features**: + * Updated the GUI for display different type of contacts (Pull requests [\#57](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/57) + * Updated and standardise Exception Handling for every command (Pull requests [\#202](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/202)) + * Updated more specific dynamic error messages and error handling for every command (Pull requests [\#215](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/215)) + +* **Project management**: + * Managed releases [v1.2](https://github.com/AY2324S2-CS2103T-W10-2/tp/releases/tag/v1.2), [v1.3(trial)](https://github.com/AY2324S2-CS2103T-W10-2/tp/releases/tag/v1.3(trial)) (2 releases) on GitHub + +* **Documentation**: + * User Guide: + * Added documentation for the features `add` + * Added documentation for the features `undo` and `redo` + * Responsible for Command Summary Table checking + * Developer Guide: + * Added non functional requirements. + * Updated use cases for PoochPlanner. + * Updated UML diagram for Storage. + * Added undo and redo details explanation for reference. + * Added undo and redo Architecture Diagram for reference. + * Updated sequence diagram to fit with latest PoochPlanner project structure. + * Responsible for Developer Guide checking. diff --git a/docs/team/jamessinmaojun.md b/docs/team/jamessinmaojun.md new file mode 100644 index 00000000000..5efa18d258e --- /dev/null +++ b/docs/team/jamessinmaojun.md @@ -0,0 +1,24 @@ +--- +layout: page +title: James' Project Portfolio Page +--- + +### Project: PoochPlanner + +PoochPlanner is a desktop application to track details of various groups (supplier, maintainer, staff) that dog cafe owners have to regularly interact with. The app is optimised for use using Command Line Interface (CLI) while still encompassing a user-friendly Graphical User Interface (GUI). + +Given below are my contributions to the project. + +* **New Feature**: Added the ability to delete contacts. + * What it does: allows the user to delete a certain contact, one at a time. + * Justification: This feature improves the product significantly because the address book can become too long and cluttered over time. Given the nature of the target users' business, contact turnover rate is expected to be very high and thus deleting unused contacts proves to be useful. + * 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}* + +* **Code contributed**: [RepoSense link]() + +* **Documentation**: + * User Guide: + * Added documentation for the feature `delete` [\#29]() + * Developer Guide: + * Added use cases for PoochPlanner [\#30]() diff --git a/docs/team/jannaleong.md b/docs/team/jannaleong.md new file mode 100644 index 00000000000..f0a2607ca7f --- /dev/null +++ b/docs/team/jannaleong.md @@ -0,0 +1,36 @@ +--- +layout: page +title: Janna's Project Portfolio Page +--- + +### Project: PoochPlanner + +PoochPlanner is a desktop application to track details of various groups (person, supplier, maintainer, staff) that dog cafe owners have to regularly interact with. The app is optimised for use using Command Line Interface (CLI) while still encompassing a user-friendly Graphical User Interface (GUI). + +Given below are my contributions to the project. + +* **New Feature**: Help Command - Added the ability to give help to users regarding commands. + * What it does: Returns the user a help window with the correct command format for a specific command or a general help window. + * Justification: This feature is crucial for first time users who are unfamiliar to the commands. + +* **New Feature**: Note Command - Added the ability to add notes to contacts. + * What it does: Adds notes to contacts. These notes have an optional deadline field. + * Justification: This feature allows dog cafe owners to remember any small detail about their contacts. + * Highlights: This feature allows optional deadline fields so cafe owners can remember their appointments and not miss a deadline. + +* **New Feature**: Remind Command - Added the ability to view reminders. + * What it does: Displays all contacts with note deadlines from today onwards. + * Justification: This feature allows dog cafe owners to be reminded of their upcoming deadlines. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2024-02-23&tabOpen=true&tabType=authorship&tabAuthor=jannaleong&tabRepo=AY2324S2-CS2103T-W10-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + +* **Documentation**: + * User Guide: + * Added documentation for the feature `help`, `note`, `remind`. + * Added common errors section so users can get help for incorrect commands easily. + * Did several rounds of checks on the entire UG to ensure all parts are standardised and follow the rubrics. + * Updated screenshots and captions to reflect the product. + * Developer Guide: + * Added target user profile, value proposition, and user stories. + * Added documentation for the feature `help`, `note`, `remind`. This includes implementation details, sequence diagrams, user stories, use cases and manual testing. + * Did several rounds of checks to ensure all parts of the DG are standardised and follow the rubrics. diff --git a/docs/team/joshy837.md b/docs/team/joshy837.md new file mode 100644 index 00000000000..5ce98a91286 --- /dev/null +++ b/docs/team/joshy837.md @@ -0,0 +1,40 @@ +--- +layout: page +title: Joshua Yip's Project Portfolio Page +--- + +### Project: PoochPlanner + +PoochPlanner is a desktop application to track details of various groups (supplier, maintainer, staff) that dog cafe owners have to regularly interact with. The app is optimised for use using Command Line Interface (CLI) while still encompassing a user-friendly Graphical User Interface (GUI). + +Given below are my contributions to the project. + +* **New Feature : Search**: Added the ability to search for any contact. + * What it does: allows the user to search a contact within a field using a specified keyword. + * Justification: This feature improves the product significantly because users can find and locate contacts in this address book without having to scroll through the entire list. + * 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. + +* **New Feature : Sort**: Added the ability to sort contacts in the contacts list. + * What it does: allows the user to sort contacts in the contacts list by a specific field such name or phone number in ascending order. + * Justification: This feature improves the product significantly because users can sort their contacts list in any ordering they prefer without having to restrict themselves to the default ordering. + * 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. + +* **New Feature : UI**: Revamped the entire user interface. + * What it does: Made the new GUI more user-friendly with more visible search boxes, rounded corners and better colour scheme. + * Justification: This feature improves the product significantly because with a revamped GUI, this user-centric design is more tailored specifically for our target audience, the dog cafe owners. + * Highlights: This enhancement replaces the old UI to favour a new enhanced version to make PoochPlanner more aesthetically pleasing for users. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s2.github.io/tp-dashboard/?search=CS2103T-W10-2&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2024-02-23&tabOpen=true&tabType=authorship&tabAuthor=Joshy837&tabRepo=AY2324S2-CS2103T-W10-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + +* **Documentation**: + * README: + * Updated README for PoochPlanner [\#15]() + * User Guide: + * Added documentation for the features `search` [\#25](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/25) + * Added documentation for the features `sort` [\#129](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/129) + * Added section on Common Errors and the respective screenshots [\#166](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/166) + * Developer Guide: + * Added Sequence Diagram for the `search` command [\#205](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/205) + * Added Sequence Diagram for the `sort` command [\#205](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/205) + * Edited the UML Diagram for `Model` [\#80](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/80) + * Responsible for Developer Guide checking diff --git a/docs/team/yleeyilin.md b/docs/team/yleeyilin.md new file mode 100644 index 00000000000..b44c1cbdd5f --- /dev/null +++ b/docs/team/yleeyilin.md @@ -0,0 +1,41 @@ +--- +layout: page +title: Yi Lin's Project Portfolio Page +--- + +### Project: PoochPlanner + +PoochPlanner is a desktop application to track details of various groups (supplier, maintainer, staff) that dog cafe owners have to regularly interact with. The app is optimised for use using Command Line Interface (CLI) while still encompassing a user-friendly Graphical User Interface (GUI). + +Given below are my contributions to the project. + +* **New Feature : Edit**: Added the ability to edit previous contacts. (Pull requests [\#62](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/62)) + * What it does: This feature allows users to edit all previous contacts. Multiple fields can be edited at the same time. + * Justification: This feature improves the product significantly because the details of a contact can change and the app should provide a convenient way to rectify the contacts. + * 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 handle all four types of contacts. + +* **New Feature : Pin**: Added the ability to pin contacts. (Pull requests [\#128](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/128)) + * What it does: This feature allows users to pin frequent contacts. Multiple contacts can be pinned. + * Justification: This feature improves the product significantly because a user may have many contacts and the app should provide a convenient way for the user to retrieve frequent contacts. + * 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 handling the versioned address book and proper storage in the json file. + +* **New Feature : Unpin**: Added the ability to unpin contacts. (Pull requests [\#128](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/128)) + * What it does: This feature allows users to unpin pinned contacts. + * Justification: This feature improves the product significantly because a user may want to pin a different contact as their priorities change and the app should provide a convenient way for the user to unpin contacts that are no longer as important.. + * 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 handling the versioned address book and proper storage in the json file. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s2.github.io/tp-dashboard/?search=CS2103T-W10-2&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2024-02-23&tabOpen=true&tabType=authorship&tabAuthor=yleeyilin&tabRepo=AY2324S2-CS2103T-W10-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + +* **Documentation**: + * User Guide: + * Added documentation for the feature `edit` [\#124](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/124) + * Added documentation for the feature `pin` [\#124](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/124) + * Added documentation for the feature `unpin` [\#124](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/124) + * Formatted documentation and added tips and constraints for all features [\#137](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/137) + * Added screenshots and explainations for all features [\#199](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/199) + * Developer Guide: + * Added use cases for PoochPlanner [\#30](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/30) + * Added Sequence Diagram for the `edit` command [\#204](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/204) + * Added Sequence Diagram for the `pin` command [\#204](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/204) + * Added Sequence Diagram for the `unpin` command [\#204](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/204) + * Edited the UML Diagram for `UI` [\#77](https://github.com/AY2324S2-CS2103T-W10-2/tp/pull/77) diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md index d98f38982e7..063ff46e430 100644 --- a/docs/tutorials/AddRemark.md +++ b/docs/tutorials/AddRemark.md @@ -293,7 +293,7 @@ While the changes to code may be minimal, the test data will have to be updated
-:exclamation: You must delete AddressBook’s storage file located at `/data/addressbook.json` before running it! Not doing so will cause AddressBook to default to an empty address book! +:exclamation: You must delete AddressBook’s storage file located at `/data/PoochPlanner.json` before running it! Not doing so will cause AddressBook to default to an empty address book!
@@ -338,7 +338,7 @@ save it with `Model#setPerson()`. List lastShownList = model.getFilteredPersonList(); if (index.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_NAME); } Person personToEdit = lastShownList.get(index.getZeroBased()); diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/address/Main.java index ec1b7958746..c8b8d203942 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/seedu/address/Main.java @@ -22,7 +22,7 @@ * to be the entry point of the application, we avoid this issue. */ public class Main { - private static Logger logger = LogsCenter.getLogger(Main.class); + private static final Logger logger = LogsCenter.getLogger(Main.class); public static void main(String[] args) { @@ -34,7 +34,6 @@ public static void main(String[] args) { // The warning however, can be safely ignored. Thus, the following log informs // the user (if looking at the log output) that the said warning appearing in the log // can be ignored. - logger.warning("The warning about Unsupported JavaFX configuration below can be ignored."); Application.launch(MainApp.class, args); } diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java deleted file mode 100644 index ecd32c31b53..00000000000 --- a/src/main/java/seedu/address/logic/Messages.java +++ /dev/null @@ -1,51 +0,0 @@ -package seedu.address.logic; - -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import seedu.address.logic.parser.Prefix; -import seedu.address.model.person.Person; - -/** - * 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_DUPLICATE_FIELDS = - "Multiple values specified for the following single-valued field(s): "; - - /** - * Returns an error message indicating the duplicate prefixes. - */ - public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePrefixes) { - assert duplicatePrefixes.length > 0; - - Set duplicateFields = - Stream.of(duplicatePrefixes).map(Prefix::toString).collect(Collectors.toSet()); - - return MESSAGE_DUPLICATE_FIELDS + String.join(" ", duplicateFields); - } - - /** - * Formats the {@code person} for display to the user. - */ - public static String format(Person person) { - final StringBuilder builder = new StringBuilder(); - builder.append(person.getName()) - .append("; Phone: ") - .append(person.getPhone()) - .append("; Email: ") - .append(person.getEmail()) - .append("; Address: ") - .append(person.getAddress()) - .append("; Tags: "); - person.getTags().forEach(builder::append); - return builder.toString(); - } - -} diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java index 5d7185a9680..edcb00dd587 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddCommand.java @@ -5,59 +5,54 @@ 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_TAG; import seedu.address.commons.util.ToStringBuilder; -import seedu.address.logic.Messages; import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.AddMessages; import seedu.address.model.Model; import seedu.address.model.person.Person; +//@@author chiageng /** * Adds a person to the address book. */ public class AddCommand extends Command { - - public static final String COMMAND_WORD = "add"; - + public static final String COMMAND_WORD = "/add-person"; public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " + "Parameters: " + PREFIX_NAME + "NAME " + PREFIX_PHONE + "PHONE " + PREFIX_EMAIL + "EMAIL " + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" + + "\n" + "Example: " + COMMAND_WORD + " " - + PREFIX_NAME + "John Doe " + + PREFIX_NAME + "John Doe other " + PREFIX_PHONE + "98765432 " + PREFIX_EMAIL + "johnd@example.com " - + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; - - 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"; + + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 "; - private final Person toAdd; + private final Person personToAdd; /** - * Creates an AddCommand to add the specified {@code Person} + * Creates an AddCommand object. + * @param person The {@code Person} to add. */ public AddCommand(Person person) { requireNonNull(person); - toAdd = person; + personToAdd = person; } @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); + if (model.hasPerson(personToAdd)) { + throw new CommandException(AddMessages.MESSAGE_ADD_DUPLICATE_PERSON); } - model.addPerson(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd))); + model.addPerson(personToAdd); + return new CommandResult(String.format(AddMessages.MESSAGE_ADD_PERSON_SUCCESS, + AddMessages.formatPerson(personToAdd))); } @Override @@ -72,13 +67,13 @@ public boolean equals(Object other) { } AddCommand otherAddCommand = (AddCommand) other; - return toAdd.equals(otherAddCommand.toAdd); + return personToAdd.equals(otherAddCommand.personToAdd); } @Override public String toString() { return new ToStringBuilder(this) - .add("toAdd", toAdd) + .add("personToAdd", personToAdd) .toString(); } } diff --git a/src/main/java/seedu/address/logic/commands/AddMaintainerCommand.java b/src/main/java/seedu/address/logic/commands/AddMaintainerCommand.java new file mode 100644 index 00000000000..db415b91855 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddMaintainerCommand.java @@ -0,0 +1,84 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMMISSION; +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_SKILL; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.AddMessages; +import seedu.address.model.Model; +import seedu.address.model.person.Maintainer; + +//@@author chiageng +/** + * Adds a maintainer to the address book. + */ +public class AddMaintainerCommand extends Command { + public static final String COMMAND_WORD = "/add-maintainer"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a maintainer to the address book. " + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_PHONE + "PHONE " + + PREFIX_EMAIL + "EMAIL " + + PREFIX_ADDRESS + "ADDRESS " + + PREFIX_SKILL + "SKILL " + + PREFIX_COMMISSION + "COMMISSION \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe trainer " + + PREFIX_PHONE + "98765432 " + + PREFIX_EMAIL + "johnd@example.com " + + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " + + PREFIX_SKILL + "train dog " + + PREFIX_COMMISSION + "$50/hr"; + + private final Maintainer maintainerToAdd; + + /** + * Creates an AddMaintainerCommand object. + * @param maintainer The {@code Maintainer} to add. + */ + public AddMaintainerCommand(Maintainer maintainer) { + requireNonNull(maintainer); + maintainerToAdd = maintainer; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasPerson(maintainerToAdd)) { + throw new CommandException(AddMessages.MESSAGE_ADD_DUPLICATE_PERSON); + } + + model.addPerson(maintainerToAdd); + return new CommandResult(String.format(AddMessages.MESSAGE_ADD_PERSON_SUCCESS, + AddMessages.formatPerson(maintainerToAdd))); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AddMaintainerCommand)) { + return false; + } + + AddMaintainerCommand otherAddCommand = (AddMaintainerCommand) other; + return maintainerToAdd.equals(otherAddCommand.maintainerToAdd); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("maintainerToAdd", maintainerToAdd) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/AddStaffCommand.java b/src/main/java/seedu/address/logic/commands/AddStaffCommand.java new file mode 100644 index 00000000000..a9c08e8176d --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddStaffCommand.java @@ -0,0 +1,84 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMPLOYMENT; +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_SALARY; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.AddMessages; +import seedu.address.model.Model; +import seedu.address.model.person.Staff; + +//@@author chiageng +/** + * Adds a staff to the address book. + */ +public class AddStaffCommand extends Command { + public static final String COMMAND_WORD = "/add-staff"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a staff to the address book. " + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_PHONE + "PHONE " + + PREFIX_EMAIL + "EMAIL " + + PREFIX_ADDRESS + "ADDRESS " + + PREFIX_SALARY + "SALARY " + + PREFIX_EMPLOYMENT + "EMPLOYMENT \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe staff " + + PREFIX_PHONE + "98765432 " + + PREFIX_EMAIL + "johnd@example.com " + + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " + + PREFIX_SALARY + "$50/hr " + + PREFIX_EMPLOYMENT + "part-time"; + + private final Staff staffToAdd; + + /** + * Creates an AddStaffCommand object. + * @param staff The {@code Staff} to add. + */ + public AddStaffCommand(Staff staff) { + requireNonNull(staff); + staffToAdd = staff; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasPerson(staffToAdd)) { + throw new CommandException(AddMessages.MESSAGE_ADD_DUPLICATE_PERSON); + } + + model.addPerson(staffToAdd); + return new CommandResult(String.format(AddMessages.MESSAGE_ADD_PERSON_SUCCESS, + AddMessages.formatPerson(staffToAdd))); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AddStaffCommand)) { + return false; + } + + AddStaffCommand otherAddCommand = (AddStaffCommand) other; + return staffToAdd.equals(otherAddCommand.staffToAdd); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("staffToAdd", staffToAdd) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/AddSupplierCommand.java b/src/main/java/seedu/address/logic/commands/AddSupplierCommand.java new file mode 100644 index 00000000000..c4ba5e5b78d --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddSupplierCommand.java @@ -0,0 +1,84 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +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_PRICE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PRODUCT; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.AddMessages; +import seedu.address.model.Model; +import seedu.address.model.person.Supplier; + +//@@author chiageng +/** + * Adds a supplier to the address book. + */ +public class AddSupplierCommand extends Command { + public static final String COMMAND_WORD = "/add-supplier"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a supplier to the address book. " + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_PHONE + "PHONE " + + PREFIX_EMAIL + "EMAIL " + + PREFIX_ADDRESS + "ADDRESS " + + PREFIX_PRODUCT + "PRODUCT " + + PREFIX_PRICE + "PRICE \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe supplier " + + PREFIX_PHONE + "98765432 " + + PREFIX_EMAIL + "johnd@example.com " + + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " + + PREFIX_PRODUCT + "poochie food " + + PREFIX_PRICE + "$50/bag"; + + private final Supplier supplierToAdd; + + /** + * Creates an AddSupplierCommand object. + * @param supplier The {@code Supplier} to add. + */ + public AddSupplierCommand(Supplier supplier) { + requireNonNull(supplier); + supplierToAdd = supplier; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasPerson(supplierToAdd)) { + throw new CommandException(AddMessages.MESSAGE_ADD_DUPLICATE_PERSON); + } + + model.addPerson(supplierToAdd); + return new CommandResult(String.format(AddMessages.MESSAGE_ADD_PERSON_SUCCESS, + AddMessages.formatPerson(supplierToAdd))); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AddSupplierCommand)) { + return false; + } + + AddSupplierCommand otherAddCommand = (AddSupplierCommand) other; + return supplierToAdd.equals(otherAddCommand.supplierToAdd); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("supplierToAdd", supplierToAdd) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java index 9c86b1fa6e4..65a208368d7 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java @@ -2,6 +2,7 @@ import static java.util.Objects.requireNonNull; +import seedu.address.logic.messages.ClearMessages; import seedu.address.model.AddressBook; import seedu.address.model.Model; @@ -9,15 +10,13 @@ * Clears the address book. */ 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 COMMAND_WORD = "/clear"; + public static final String MESSAGE_SUCCESS = "Woof! Cleared PoochPlanner successfully! 🐶"; @Override public CommandResult execute(Model model) { requireNonNull(model); model.setAddressBook(new AddressBook()); - return new CommandResult(MESSAGE_SUCCESS); + return new CommandResult(ClearMessages.MESSAGE_CLEAR_POOCHPLANNER_SUCCESS); } } diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java index 1135ac19b74..15ee327b458 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java @@ -1,48 +1,49 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import java.util.List; - -import seedu.address.commons.core.index.Index; import seedu.address.commons.util.ToStringBuilder; -import seedu.address.logic.Messages; import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.DeleteMessages; import seedu.address.model.Model; +import seedu.address.model.person.Name; import seedu.address.model.person.Person; +//@@author jamessinmaojun /** - * Deletes a person identified using it's displayed index from the address book. + * Deletes a person identified using it's displayed name from the address book. */ public class DeleteCommand extends Command { - - public static final String COMMAND_WORD = "delete"; - + public static final String COMMAND_WORD = "/delete"; public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Deletes the person identified by the index number used in the displayed person 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"; - - private final Index targetIndex; - - public DeleteCommand(Index targetIndex) { - this.targetIndex = targetIndex; + + ": Deletes the person identified by their name.\n" + + "Parameters: " + + PREFIX_NAME + "NAME " + + "Example: " + COMMAND_WORD + " " + PREFIX_NAME + "Moochie"; + + private final Name nameToDelete; + + /** + * Creates a DeleteCommand object. + * @param name Name of the person in the person list to delete. + */ + public DeleteCommand(Name name) { + this.nameToDelete = name; } @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - if (targetIndex.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } + Person personToDelete; + + personToDelete = model.findByName(nameToDelete, DeleteMessages.MESSAGE_DELETE_NAME_NOT_FOUND); - Person personToDelete = lastShownList.get(targetIndex.getZeroBased()); model.deletePerson(personToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete))); + + return new CommandResult(String.format(DeleteMessages.MESSAGE_DELETE_PERSON_SUCCESS, + DeleteMessages.formatPerson(personToDelete))); } @Override @@ -57,13 +58,13 @@ public boolean equals(Object other) { } DeleteCommand otherDeleteCommand = (DeleteCommand) other; - return targetIndex.equals(otherDeleteCommand.targetIndex); + return nameToDelete.equals(otherDeleteCommand.nameToDelete); } @Override public String toString() { return new ToStringBuilder(this) - .add("targetIndex", targetIndex) + .add("nameToDelete", nameToDelete) .toString(); } } diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java index 4b581c7331e..4fd4be5f0d5 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/address/logic/commands/EditCommand.java @@ -1,96 +1,93 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_FIELD; 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_TAG; import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.logging.Logger; -import seedu.address.commons.core.index.Index; +import seedu.address.commons.core.LogsCenter; import seedu.address.commons.util.CollectionUtil; import seedu.address.commons.util.ToStringBuilder; -import seedu.address.logic.Messages; import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.EditMessages; 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.Note; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Rating; import seedu.address.model.tag.Tag; +//@@author yleeyilin /** - * Edits the details of an existing person in the address book. + * Edits the details of an existing person in PoochPlanner. */ 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. " - + "Existing values will be overwritten by the input values.\n" - + "Parameters: INDEX (must be a positive integer) " + public static final String COMMAND_WORD = "/edit-person"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Main Parameters: " + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_FIELD + "FIELD] \n" + + "Field Parameters: " + "[" + PREFIX_PHONE + "PHONE] " - + "[" + PREFIX_EMAIL + "EMAIL] " + "[" + PREFIX_ADDRESS + "ADDRESS] " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " 1 " - + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; - - public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %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."; - - private final Index index; + + "[" + PREFIX_EMAIL + "EMAIL] \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe Others " + + PREFIX_FIELD + "{ " + + "phone : " + "99820550 " + + PREFIX_ADDRESS + "NUS College Avenue" + + " }"; + public static final String MESSAGE_NULL_NAME = "Specified name to edit person is null."; + + private final Name name; private final EditPersonDescriptor editPersonDescriptor; + private final Logger logger = LogsCenter.getLogger(getClass()); /** - * @param index of the person in the filtered person list to edit - * @param editPersonDescriptor details to edit the person with + * @param name Name of the person in the filtered person list to edit. + * @param editPersonDescriptor Details to edit the person with. */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { - requireNonNull(index); - requireNonNull(editPersonDescriptor); - - this.index = index; + public EditCommand(Name name, EditPersonDescriptor editPersonDescriptor) { + requireAllNonNull(name, editPersonDescriptor); + this.name = name; this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); } @Override public CommandResult execute(Model model) throws CommandException { + assert (name != null) : MESSAGE_NULL_NAME; requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - - if (index.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - Person personToEdit = lastShownList.get(index.getZeroBased()); + Person personToEdit = model.findPersonByName(name, EditMessages.MESSAGE_INVALID_EDIT_PERSON); Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - model.setPerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson))); + model.updateFilteredPersonListWithCommit(PREDICATE_SHOW_ALL_PERSONS); + + logger.fine(String.format(EditMessages.MESSAGE_EDIT_PERSON_SUCCESS, + EditMessages.formatPerson(editedPerson))); + + return new CommandResult(String.format(EditMessages.MESSAGE_EDIT_PERSON_SUCCESS, + EditMessages.formatPerson(editedPerson))); } /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. + * Creates and returns a {@code Person} with the details of {@code personToEdit}. + * Edited with {@code editPersonDescriptor}. */ private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { assert personToEdit != null; @@ -99,9 +96,18 @@ private static Person createEditedPerson(Person personToEdit, EditPersonDescript Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); + Note presentNote = personToEdit.getNote(); //edit cannot change note + Rating presentRating = personToEdit.getRating(); //edit cannot change rating Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); + Person updatedPerson = new Person(updatedName, + updatedPhone, updatedEmail, updatedAddress, presentNote, updatedTags, presentRating); + + if (personToEdit.isPinned()) { + updatedPerson.toPin(); + } + + return updatedPerson; } @Override @@ -110,20 +116,20 @@ public boolean equals(Object other) { return true; } - // instanceof handles nulls if (!(other instanceof EditCommand)) { return false; } EditCommand otherEditCommand = (EditCommand) other; - return index.equals(otherEditCommand.index) - && editPersonDescriptor.equals(otherEditCommand.editPersonDescriptor); + boolean areNamesEqual = name.equals(otherEditCommand.name); + boolean areDescriptorsEqual = editPersonDescriptor.equals(otherEditCommand.editPersonDescriptor); + return areNamesEqual && areDescriptorsEqual; } @Override public String toString() { return new ToStringBuilder(this) - .add("index", index) + .add("name", name) .add("editPersonDescriptor", editPersonDescriptor) .toString(); } @@ -221,17 +227,18 @@ public boolean equals(Object other) { } EditPersonDescriptor otherEditPersonDescriptor = (EditPersonDescriptor) other; - return Objects.equals(name, otherEditPersonDescriptor.name) - && Objects.equals(phone, otherEditPersonDescriptor.phone) - && Objects.equals(email, otherEditPersonDescriptor.email) - && Objects.equals(address, otherEditPersonDescriptor.address) - && Objects.equals(tags, otherEditPersonDescriptor.tags); + + boolean phoneEquals = Objects.equals(phone, otherEditPersonDescriptor.phone); + boolean emailEquals = Objects.equals(email, otherEditPersonDescriptor.email); + boolean addressEquals = Objects.equals(address, otherEditPersonDescriptor.address); + boolean tagsEquals = Objects.equals(tags, otherEditPersonDescriptor.tags); + + return phoneEquals && emailEquals && addressEquals && tagsEquals; } @Override public String toString() { return new ToStringBuilder(this) - .add("name", name) .add("phone", phone) .add("email", email) .add("address", address) diff --git a/src/main/java/seedu/address/logic/commands/EditMaintainerCommand.java b/src/main/java/seedu/address/logic/commands/EditMaintainerCommand.java new file mode 100644 index 00000000000..eb2d4529d10 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/EditMaintainerCommand.java @@ -0,0 +1,287 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMMISSION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_FIELD; +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_SKILL; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.EditMessages; +import seedu.address.model.Model; +import seedu.address.model.person.Address; +import seedu.address.model.person.Commission; +import seedu.address.model.person.Email; +import seedu.address.model.person.Maintainer; +import seedu.address.model.person.Name; +import seedu.address.model.person.Note; +import seedu.address.model.person.Phone; +import seedu.address.model.person.Rating; +import seedu.address.model.person.Skill; +import seedu.address.model.tag.Tag; + +//@@author yleeyilin +/** + * Edits the details of an existing maintainer in PoochPlanner. + */ +public class EditMaintainerCommand extends Command { + public static final String COMMAND_WORD = "/edit-maintainer"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Main Parameters: " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_FIELD + "FIELD] \n" + + "Field Parameters: " + + "[" + PREFIX_PHONE + "PHONE] " + + "[" + PREFIX_ADDRESS + "ADDRESS] " + + "[" + PREFIX_EMAIL + "EMAIL] " + + "[" + PREFIX_SKILL + "SKILL] " + + "[" + PREFIX_COMMISSION + "COMMISSION] \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe Maintainer " + + PREFIX_FIELD + "{ " + + "phone : " + "99820550 " + + PREFIX_ADDRESS + "NUS College Avenue" + + " }"; + public static final String MESSAGE_NULL_NAME = "Specified name to edit maintainer is null."; + + private final Name name; + private final EditMaintainerDescriptor editMaintainerDescriptor; + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * @param name Name of the maintainer in the filtered person list to edit. + * @param editMaintainerDescriptor Details to edit the maintainer with. + */ + public EditMaintainerCommand(Name name, EditMaintainerDescriptor editMaintainerDescriptor) { + requireAllNonNull(name, editMaintainerDescriptor); + this.name = name; + this.editMaintainerDescriptor = new EditMaintainerDescriptor(editMaintainerDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + assert (name != null) : MESSAGE_NULL_NAME; + requireNonNull(model); + + Maintainer maintainerToEdit = model.findMaintainerByName(name, + EditMessages.MESSAGE_INVALID_EDIT_MAINTAINER); + Maintainer editedMaintainer = createEditedMaintainer(maintainerToEdit, editMaintainerDescriptor); + + model.setPerson(maintainerToEdit, editedMaintainer); + model.updateFilteredPersonListWithCommit(PREDICATE_SHOW_ALL_PERSONS); + + logger.fine(String.format(EditMessages.MESSAGE_EDIT_PERSON_SUCCESS, + EditMessages.formatPerson(editedMaintainer))); + + return new CommandResult(String.format(EditMessages.MESSAGE_EDIT_PERSON_SUCCESS, + EditMessages.formatPerson(editedMaintainer))); + } + + /** + * Creates and returns a {@code Maintainer} with the details of {@code maintainerToEdit} + * edited with {@code editMaintainerDescriptor}. + */ + private static Maintainer createEditedMaintainer(Maintainer maintainerToEdit, + EditMaintainerDescriptor editMaintainerDescriptor) { + assert maintainerToEdit != null; + + Name updatedName = editMaintainerDescriptor.getName().orElse(maintainerToEdit.getName()); + Phone updatedPhone = editMaintainerDescriptor.getPhone().orElse(maintainerToEdit.getPhone()); + Email updatedEmail = editMaintainerDescriptor.getEmail().orElse(maintainerToEdit.getEmail()); + Address updatedAddress = editMaintainerDescriptor.getAddress().orElse(maintainerToEdit.getAddress()); + Note presentNote = maintainerToEdit.getNote(); //edit cannot change note + Rating presentRating = maintainerToEdit.getRating(); //edit cannot change rating + Set updatedTags = editMaintainerDescriptor.getTags().orElse(maintainerToEdit.getTags()); + Skill updatedSkill = editMaintainerDescriptor.getSkill().orElse(maintainerToEdit.getSkill()); + Commission updatedCommission = editMaintainerDescriptor.getCommission() + .orElse(maintainerToEdit.getCommission()); + + Maintainer updatedMaintainer = new Maintainer(updatedName, updatedPhone, updatedEmail, updatedAddress, + presentNote, updatedTags, updatedSkill, updatedCommission, presentRating); + + if (maintainerToEdit.isPinned()) { + updatedMaintainer.toPin(); + } + + return updatedMaintainer; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditMaintainerCommand)) { + return false; + } + + EditMaintainerCommand otherEditMaintainerCommand = (EditMaintainerCommand) other; + boolean areNamesEqual = name.equals(otherEditMaintainerCommand.name); + boolean areDescriptorsEqual = + editMaintainerDescriptor.equals(otherEditMaintainerCommand.editMaintainerDescriptor); + return areNamesEqual && areDescriptorsEqual; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("name", name) + .add("editMaintainerDescriptor", editMaintainerDescriptor) + .toString(); + } + + /** + * Stores the details to edit the maintainer with. Each non-empty field value will replace the + * corresponding field value of the maintainer. + */ + public static class EditMaintainerDescriptor { + private Name name; + private Phone phone; + private Email email; + private Address address; + private Set tags; + private Skill skill; + private Commission commission; + + public EditMaintainerDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditMaintainerDescriptor(EditMaintainerDescriptor toCopy) { + setName(toCopy.name); + setPhone(toCopy.phone); + setEmail(toCopy.email); + setAddress(toCopy.address); + setTags(toCopy.tags); + setSkill(toCopy.skill); + setCommission(toCopy.commission); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(name, phone, email, address, tags, skill, commission); + } + + public void setName(Name name) { + this.name = name; + } + + public Optional getName() { + return Optional.ofNullable(name); + } + + public void setPhone(Phone phone) { + this.phone = phone; + } + + public Optional getPhone() { + return Optional.ofNullable(phone); + } + + public void setEmail(Email email) { + this.email = email; + } + + public Optional getEmail() { + return Optional.ofNullable(email); + } + + public void setAddress(Address address) { + this.address = address; + } + + public Optional
getAddress() { + return Optional.ofNullable(address); + } + + public void setSkill(Skill skill) { + this.skill = skill; + } + + public Optional getSkill() { + return Optional.ofNullable(skill); + } + + public void setCommission(Commission commission) { + this.commission = commission; + } + + public Optional getCommission() { + return Optional.ofNullable(commission); + } + + /** + * Sets {@code tags} to this object's {@code tags}. + * A defensive copy of {@code tags} is used internally. + */ + public void setTags(Set tags) { + this.tags = (tags != null) ? new HashSet<>(tags) : 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> getTags() { + return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof EditMaintainerDescriptor)) { + return false; + } + + EditMaintainerDescriptor otherEditMaintainerDescriptor = (EditMaintainerDescriptor) other; + + boolean arePhoneEqual = Objects.equals(phone, otherEditMaintainerDescriptor.phone); + boolean areEmailEqual = Objects.equals(email, otherEditMaintainerDescriptor.email); + boolean areAddressEqual = Objects.equals(address, otherEditMaintainerDescriptor.address); + boolean areTagsEqual = Objects.equals(tags, otherEditMaintainerDescriptor.tags); + boolean areSkillEqual = Objects.equals(skill, otherEditMaintainerDescriptor.skill); + boolean areCommissionEqual = Objects.equals(commission, + otherEditMaintainerDescriptor.commission); + + return arePhoneEqual && areEmailEqual && areAddressEqual + && areTagsEqual && areSkillEqual && areCommissionEqual; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("phone", phone) + .add("email", email) + .add("address", address) + .add("tags", tags) + .add("skill", skill) + .add("commission", commission) + .toString(); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditStaffCommand.java b/src/main/java/seedu/address/logic/commands/EditStaffCommand.java new file mode 100644 index 00000000000..fd400bcaf62 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/EditStaffCommand.java @@ -0,0 +1,283 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMPLOYMENT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_FIELD; +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_SALARY; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.EditMessages; +import seedu.address.model.Model; +import seedu.address.model.person.Address; +import seedu.address.model.person.Email; +import seedu.address.model.person.Employment; +import seedu.address.model.person.Name; +import seedu.address.model.person.Note; +import seedu.address.model.person.Phone; +import seedu.address.model.person.Rating; +import seedu.address.model.person.Salary; +import seedu.address.model.person.Staff; +import seedu.address.model.tag.Tag; + +//@@author yleeyilin +/** + * Edits the details of an existing staff in PoochPlanner. + */ +public class EditStaffCommand extends Command { + public static final String COMMAND_WORD = "/edit-staff"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Main Parameters: " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_FIELD + "FIELD] \n" + + "Field Parameters: " + + "[" + PREFIX_PHONE + "PHONE] " + + "[" + PREFIX_ADDRESS + "ADDRESS] " + + "[" + PREFIX_EMAIL + "EMAIL] " + + "[" + PREFIX_SALARY + "SALARY] " + + "[" + PREFIX_EMPLOYMENT + "EMPLOYMENT] \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe Staff " + + PREFIX_FIELD + "{ " + + "phone : " + "99820550 " + + PREFIX_ADDRESS + "NUS College Avenue" + + " }"; + public static final String MESSAGE_NULL_NAME = "Specified name to edit staff is null."; + + private final Name name; + private final EditStaffDescriptor editStaffDescriptor; + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * @param name Name of the staff in the filtered person list to edit. + * @param editStaffDescriptor Details to edit the staff with. + */ + public EditStaffCommand(Name name, EditStaffDescriptor editStaffDescriptor) { + requireAllNonNull(name, editStaffDescriptor); + this.name = name; + this.editStaffDescriptor = new EditStaffDescriptor(editStaffDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + assert (name != null) : MESSAGE_NULL_NAME; + requireNonNull(model); + + Staff staffToEdit = model.findStaffByName(name, + EditMessages.MESSAGE_INVALID_EDIT_STAFF); + Staff editedStaff = createEditedStaff(staffToEdit, editStaffDescriptor); + + model.setPerson(staffToEdit, editedStaff); + model.updateFilteredPersonListWithCommit(PREDICATE_SHOW_ALL_PERSONS); + + logger.fine(String.format(EditMessages.MESSAGE_EDIT_PERSON_SUCCESS, + EditMessages.formatPerson(editedStaff))); + + return new CommandResult(String.format(EditMessages.MESSAGE_EDIT_PERSON_SUCCESS, + EditMessages.formatPerson(editedStaff))); + } + + /** + * Creates and returns a {@code Staff} with the details of {@code staffToEdit} + * edited with {@code editStaffDescriptor}. + */ + private static Staff createEditedStaff(Staff staffToEdit, EditStaffDescriptor editStaffDescriptor) { + assert staffToEdit != null; + + Name updatedName = editStaffDescriptor.getName().orElse(staffToEdit.getName()); + Phone updatedPhone = editStaffDescriptor.getPhone().orElse(staffToEdit.getPhone()); + Email updatedEmail = editStaffDescriptor.getEmail().orElse(staffToEdit.getEmail()); + Address updatedAddress = editStaffDescriptor.getAddress().orElse(staffToEdit.getAddress()); + Note presentNote = staffToEdit.getNote(); //edit cannot change note + Rating presentRating = staffToEdit.getRating(); //edit cannot change rating + Set updatedTags = editStaffDescriptor.getTags().orElse(staffToEdit.getTags()); + Salary updatedSalary = editStaffDescriptor.getSalary().orElse(staffToEdit.getSalary()); + Employment updatedEmployment = editStaffDescriptor.getEmployment().orElse(staffToEdit.getEmployment()); + + Staff updatedStaff = new Staff(updatedName, updatedPhone, updatedEmail, updatedAddress, + presentNote, updatedTags, updatedSalary, updatedEmployment, presentRating); + + if (staffToEdit.isPinned()) { + updatedStaff.toPin(); + } + + return updatedStaff; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditStaffCommand)) { + return false; + } + + EditStaffCommand otherEditStaffCommand = (EditStaffCommand) other; + boolean areNamesEqual = name.equals(otherEditStaffCommand.name); + boolean areDescriptorsEqual = editStaffDescriptor.equals(otherEditStaffCommand.editStaffDescriptor); + return areNamesEqual && areDescriptorsEqual; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("name", name) + .add("editStaffDescriptor", editStaffDescriptor) + .toString(); + } + + /** + * Stores the details to edit the staff with. Each non-empty field value will replace the + * corresponding field value of the staff. + */ + public static class EditStaffDescriptor { + private Name name; + private Phone phone; + private Email email; + private Address address; + private Set tags; + private Salary salary; + private Employment employment; + + public EditStaffDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditStaffDescriptor(EditStaffDescriptor toCopy) { + setName(toCopy.name); + setPhone(toCopy.phone); + setEmail(toCopy.email); + setAddress(toCopy.address); + setTags(toCopy.tags); + setSalary(toCopy.salary); + setEmployment(toCopy.employment); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(name, phone, email, address, tags, salary, employment); + } + + public void setName(Name name) { + this.name = name; + } + + public Optional getName() { + return Optional.ofNullable(name); + } + + public void setPhone(Phone phone) { + this.phone = phone; + } + + public Optional getPhone() { + return Optional.ofNullable(phone); + } + + public void setEmail(Email email) { + this.email = email; + } + + public Optional getEmail() { + return Optional.ofNullable(email); + } + + public void setAddress(Address address) { + this.address = address; + } + + public Optional
getAddress() { + return Optional.ofNullable(address); + } + + public void setSalary(Salary salary) { + this.salary = salary; + } + + public Optional getSalary() { + return Optional.ofNullable(salary); + } + + public void setEmployment(Employment employment) { + this.employment = employment; + } + + public Optional getEmployment() { + return Optional.ofNullable(employment); + } + + /** + * Sets {@code tags} to this object's {@code tags}. + * A defensive copy of {@code tags} is used internally. + */ + public void setTags(Set tags) { + this.tags = (tags != null) ? new HashSet<>(tags) : 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> getTags() { + return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof EditStaffDescriptor)) { + return false; + } + + EditStaffDescriptor otherEditStaffDescriptor = (EditStaffDescriptor) other; + + boolean arePhoneEqual = Objects.equals(phone, otherEditStaffDescriptor.phone); + boolean areEmailEqual = Objects.equals(email, otherEditStaffDescriptor.email); + boolean areAddressEqual = Objects.equals(address, otherEditStaffDescriptor.address); + boolean areTagsEqual = Objects.equals(tags, otherEditStaffDescriptor.tags); + boolean areSalaryEqual = Objects.equals(salary, otherEditStaffDescriptor.salary); + boolean areEmploymentEqual = Objects.equals(employment, otherEditStaffDescriptor.employment); + + return arePhoneEqual && areEmailEqual && areAddressEqual + && areTagsEqual && areSalaryEqual && areEmploymentEqual; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("phone", phone) + .add("email", email) + .add("address", address) + .add("tags", tags) + .add("salary", salary) + .add("employment", employment) + .toString(); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditSupplierCommand.java b/src/main/java/seedu/address/logic/commands/EditSupplierCommand.java new file mode 100644 index 00000000000..48d820b07a4 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/EditSupplierCommand.java @@ -0,0 +1,284 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_FIELD; +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_PRICE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PRODUCT; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.EditMessages; +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.Note; +import seedu.address.model.person.Phone; +import seedu.address.model.person.Price; +import seedu.address.model.person.Product; +import seedu.address.model.person.Rating; +import seedu.address.model.person.Supplier; +import seedu.address.model.tag.Tag; + +//@@author yleeyilin +/** + * Edits the details of an existing supplier in PoochPlanner. + */ +public class EditSupplierCommand extends Command { + public static final String COMMAND_WORD = "/edit-supplier"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Main Parameters: " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_FIELD + "FIELD] \n" + + "Field Parameters: " + + "[" + PREFIX_PHONE + "PHONE] " + + "[" + PREFIX_ADDRESS + "ADDRESS] " + + "[" + PREFIX_EMAIL + "EMAIL] " + + "[" + PREFIX_PRODUCT + "PRODUCT] " + + "[" + PREFIX_PRICE + "PRICE] \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe Supplier " + + PREFIX_FIELD + "{ " + + "phone : " + "99820550 " + + PREFIX_ADDRESS + "NUS College Avenue" + + " }"; + public static final String MESSAGE_NULL_NAME = "Specified name to edit supplier is null."; + + private final Name name; + private final EditSupplierDescriptor editSupplierDescriptor; + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * @param name Name of the supplier in the filtered person list to edit. + * @param editSupplierDescriptor Details to edit the supplier with. + */ + public EditSupplierCommand(Name name, EditSupplierDescriptor editSupplierDescriptor) { + requireAllNonNull(name, editSupplierDescriptor); + + this.name = name; + this.editSupplierDescriptor = new EditSupplierDescriptor(editSupplierDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + assert (name != null) : MESSAGE_NULL_NAME; + requireNonNull(model); + + Supplier supplierToEdit = model.findSupplierByName(name, + EditMessages.MESSAGE_INVALID_EDIT_SUPPLIER); + Supplier editedSupplier = createEditedSupplier(supplierToEdit, editSupplierDescriptor); + + model.setPerson(supplierToEdit, editedSupplier); + model.updateFilteredPersonListWithCommit(PREDICATE_SHOW_ALL_PERSONS); + + logger.fine(String.format(EditMessages.MESSAGE_EDIT_PERSON_SUCCESS, + EditMessages.formatPerson(editedSupplier))); + + return new CommandResult(String.format(EditMessages.MESSAGE_EDIT_PERSON_SUCCESS, + EditMessages.formatPerson(editedSupplier))); + } + + /** + * Creates and returns a {@code Supplier} with the details of {@code supplierToEdit} + * edited with {@code editSupplierDescriptor}. + */ + private static Supplier createEditedSupplier(Supplier supplierToEdit, + EditSupplierDescriptor editSupplierDescriptor) { + assert supplierToEdit != null; + + Name updatedName = editSupplierDescriptor.getName().orElse(supplierToEdit.getName()); + Phone updatedPhone = editSupplierDescriptor.getPhone().orElse(supplierToEdit.getPhone()); + Email updatedEmail = editSupplierDescriptor.getEmail().orElse(supplierToEdit.getEmail()); + Address updatedAddress = editSupplierDescriptor.getAddress().orElse(supplierToEdit.getAddress()); + Note presentNote = supplierToEdit.getNote(); //edit cannot change note + Rating presentRating = supplierToEdit.getRating(); //edit cannot change rating + Set updatedTags = editSupplierDescriptor.getTags().orElse(supplierToEdit.getTags()); + Product updatedProduct = editSupplierDescriptor.getProduct().orElse(supplierToEdit.getProduct()); + Price updatedPrice = editSupplierDescriptor.getPrice().orElse(supplierToEdit.getPrice()); + + Supplier updatedSupplier = new Supplier(updatedName, updatedPhone, updatedEmail, updatedAddress, presentNote, + updatedTags, updatedProduct, updatedPrice, presentRating); + + if (supplierToEdit.isPinned()) { + updatedSupplier.toPin(); + } + return updatedSupplier; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditSupplierCommand)) { + return false; + } + + EditSupplierCommand otherEditSupplierCommand = (EditSupplierCommand) other; + boolean areNamesEqual = name.equals(otherEditSupplierCommand.name); + boolean areDescriptorsEqual = editSupplierDescriptor.equals(otherEditSupplierCommand.editSupplierDescriptor); + return areNamesEqual && areDescriptorsEqual; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("name", name) + .add("editSupplierDescriptor", editSupplierDescriptor) + .toString(); + } + + /** + * Stores the details to edit the supplier with. Each non-empty field value will replace the + * corresponding field value of the supplier. + */ + public static class EditSupplierDescriptor { + private Name name; + private Phone phone; + private Email email; + private Address address; + private Set tags; + private Product product; + private Price price; + + public EditSupplierDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditSupplierDescriptor(EditSupplierDescriptor toCopy) { + setName(toCopy.name); + setPhone(toCopy.phone); + setEmail(toCopy.email); + setAddress(toCopy.address); + setTags(toCopy.tags); + setProduct(toCopy.product); + setPrice(toCopy.price); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(name, phone, email, address, tags, product, price); + } + + public void setName(Name name) { + this.name = name; + } + + public Optional getName() { + return Optional.ofNullable(name); + } + + public void setPhone(Phone phone) { + this.phone = phone; + } + + public Optional getPhone() { + return Optional.ofNullable(phone); + } + + public void setEmail(Email email) { + this.email = email; + } + + public Optional getEmail() { + return Optional.ofNullable(email); + } + + public void setAddress(Address address) { + this.address = address; + } + + public Optional
getAddress() { + return Optional.ofNullable(address); + } + + public void setProduct(Product product) { + this.product = product; + } + + public Optional getProduct() { + return Optional.ofNullable(product); + } + + public void setPrice(Price price) { + this.price = price; + } + + public Optional getPrice() { + return Optional.ofNullable(price); + } + + /** + * Sets {@code tags} to this object's {@code tags}. + * A defensive copy of {@code tags} is used internally. + */ + public void setTags(Set tags) { + this.tags = (tags != null) ? new HashSet<>(tags) : 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> getTags() { + return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditSupplierDescriptor)) { + return false; + } + + EditSupplierDescriptor otherEditSupplierDescriptor = (EditSupplierDescriptor) other; + + boolean phoneEquals = Objects.equals(phone, otherEditSupplierDescriptor.phone); + boolean emailEquals = Objects.equals(email, otherEditSupplierDescriptor.email); + boolean addressEquals = Objects.equals(address, otherEditSupplierDescriptor.address); + boolean tagsEquals = Objects.equals(tags, otherEditSupplierDescriptor.tags); + boolean productEquals = Objects.equals(tags, otherEditSupplierDescriptor.tags); + boolean priceEquals = Objects.equals(tags, otherEditSupplierDescriptor.tags); + + return phoneEquals && emailEquals && addressEquals && tagsEquals && productEquals && priceEquals; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("phone", phone) + .add("email", email) + .add("address", address) + .add("tags", tags) + .add("product", product) + .add("price", product) + .toString(); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java index 3dd85a8ba90..70703fd7595 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java @@ -7,7 +7,7 @@ */ public class ExitCommand extends Command { - public static final String COMMAND_WORD = "exit"; + public static final String COMMAND_WORD = "/exit"; public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java deleted file mode 100644 index 72b9eddd3a7..00000000000 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ /dev/null @@ -1,58 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import seedu.address.commons.util.ToStringBuilder; -import seedu.address.logic.Messages; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -/** - * Finds and lists all persons 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 " - + "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"; - - private final NameContainsKeywordsPredicate predicate; - - public FindCommand(NameContainsKeywordsPredicate predicate) { - this.predicate = predicate; - } - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.updateFilteredPersonList(predicate); - return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof FindCommand)) { - return false; - } - - FindCommand otherFindCommand = (FindCommand) other; - return predicate.equals(otherFindCommand.predicate); - } - - @Override - public String toString() { - return new ToStringBuilder(this) - .add("predicate", predicate) - .toString(); - } -} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java index bf824f91bd0..2f20bed576b 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java @@ -1,21 +1,151 @@ package seedu.address.logic.commands; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.HelpMessages; import seedu.address.model.Model; +//@@author jannaleong /** - * Format full help instructions for every command for display. + * Returns help details for each command. */ public class HelpCommand extends Command { + public static final String COMMAND_WORD = "/help"; + public static final String MESSAGE_USAGE = "Shows program usage instructions.\n" + + "Example: /help ; command : exit"; + public static final String MESSAGE_CONSTRAINTS = "PoochPlanner only accepts general, add, delete, edit," + + " exit, search, list, note, pin, unpin, note, rate, redo, undo, remind, sort and clear as" + + " valid command type inputs."; + public static final String MESSAGE_NULL_COMMAND = "Specified command type to give help for is null."; + public static final String LOGGER_EXECUTE_HELP_MESSAGE = "Started executing the help command."; + + private final String commandType; + private final Logger logger = LogsCenter.getLogger(getClass()); + + private enum CommandTypes { + GENERAL, + ADD, + DELETE, + EDIT, + EXIT, + SEARCH, + LIST, + NOTE, + PIN, + UNPIN, + RATE, + REDO, + UNDO, + REMIND, + SORT, + CLEAR + } + + /** + * Constructs a HelpCommand object. + * @param commandType The command type to give help for. + */ + public HelpCommand(String commandType) { + requireAllNonNull(commandType); + this.commandType = commandType; + } + + /** + * Checks if command provided is a valid command. + * Parameter commandType cannot be null. + * + * @param commandType The command type to give help for. + * @return Boolean showing whether the command type is valid. + * */ + public static boolean isValidCommandType(String commandType) { + assert (commandType != null) : MESSAGE_NULL_COMMAND; - public static final String COMMAND_WORD = "help"; + for (CommandTypes c : CommandTypes.values()) { + String lowercaseName = c.name().toLowerCase(); + String lowercaseCommandType = commandType.toLowerCase(); + if (lowercaseName.equals(lowercaseCommandType)) { + return true; + } + } + return false; + } + + /** + * Executes the command and returns the help message. + * Field commandType cannot be null. + * + * @param model {@code Model} which the command should operate on. + * @return Feedback message of the operation result for display. + * @throws CommandException If an error occurs during command execution. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + assert (commandType != null) : MESSAGE_NULL_COMMAND; + logger.info(LOGGER_EXECUTE_HELP_MESSAGE); + + // return the correct help message based on command type + String capitalizedCommandType = commandType.toUpperCase(); + if (capitalizedCommandType.equals(CommandTypes.GENERAL.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.ADD.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_ADD_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.DELETE.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_DELETE_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.EDIT.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_EDIT_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.SEARCH.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_SEARCH_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.EXIT.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_EXIT_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.LIST.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_LIST_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.NOTE.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_NOTE_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.PIN.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_PIN_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.UNPIN.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_UNPIN_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.RATE.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_RATE_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.REDO.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_REDO_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.UNDO.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_UNDO_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.REMIND.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_REMIND_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.SORT.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_SORT_HELP_MESSAGE, true, false); + } else if (capitalizedCommandType.equals(CommandTypes.CLEAR.name())) { + return new CommandResult(HelpMessages.MESSAGES_SHOWING_CLEAR_HELP_MESSAGE, true, false); + } else { + throw new CommandException(HelpMessages.MESSAGES_INVALID_COMMAND_TYPE); + } + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions.\n" - + "Example: " + COMMAND_WORD; + // instanceof handles nulls + if (!(other instanceof HelpCommand)) { + return false; + } - public static final String SHOWING_HELP_MESSAGE = "Opened help window."; + HelpCommand otherHelpCommand = (HelpCommand) other; + return commandType.equals(otherHelpCommand.commandType); + } @Override - public CommandResult execute(Model model) { - return new CommandResult(SHOWING_HELP_MESSAGE, true, false); + public String toString() { + return new ToStringBuilder(this) + .add("command type", commandType) + .toString(); } } diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java index 84be6ad2596..25b4a6d78cf 100644 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ b/src/main/java/seedu/address/logic/commands/ListCommand.java @@ -3,22 +3,19 @@ import static java.util.Objects.requireNonNull; import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import seedu.address.logic.messages.ListMessages; import seedu.address.model.Model; /** * Lists all persons 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 COMMAND_WORD = "/list"; @Override public CommandResult execute(Model model) { requireNonNull(model); model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(MESSAGE_SUCCESS); + return new CommandResult(ListMessages.MESSAGE_LIST_CONTACTS_SUCCESS); } } diff --git a/src/main/java/seedu/address/logic/commands/NoteCommand.java b/src/main/java/seedu/address/logic/commands/NoteCommand.java new file mode 100644 index 00000000000..d9e345882a7 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/NoteCommand.java @@ -0,0 +1,94 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.Messages; +import seedu.address.logic.messages.NoteMessages; +import seedu.address.model.Model; +import seedu.address.model.person.Name; +import seedu.address.model.person.Note; +import seedu.address.model.person.Person; + +//@@author jannaleong +/** + * Adds a note to an existing person in PoochPlanner. + * A non-empty note and name must be specified. + */ +public class NoteCommand extends Command { + public static final String COMMAND_WORD = "/note"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Adds note to a contact in PoochPlanner.\n" + + "Parameters: " + + PREFIX_NAME + "NAME" + + PREFIX_NOTE + "NOTE" + + "\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_NAME + + " Moochie" + " " + PREFIX_NOTE + "Meet at 6pm Tuesday"; + public static final String MESSAGE_NULL_NAME = "Specified name to add to contact is null."; + public static final String MESSAGE_NULL_NOTE = "Specified note to add to contact is null."; + public static final String LOGGER_EXECUTE_NOTE_MESSAGE = "Started executing the note command."; + + private final Name name; + private final Note note; + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Constructs a NoteCommand object. + * @param name Name of the person in PoochPlanner to add note to. + * @param note Note to add to the specified person in PoochPlanner. + */ + public NoteCommand(Name name, Note note) { + requireAllNonNull(name, note); + this.name = name; + this.note = note; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + assert (note != null) : MESSAGE_NULL_NOTE; + assert (name != null) : MESSAGE_NULL_NAME; + logger.info(LOGGER_EXECUTE_NOTE_MESSAGE); + requireNonNull(model); + + Person personToEdit = model.findByName(name, NoteMessages.MESSAGE_NOTE_NAME_NOT_FOUND); + Person editedPerson = personToEdit.updateNote(note); + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonListWithCommit(PREDICATE_SHOW_ALL_PERSONS); + + return new CommandResult(String.format(NoteMessages.MESSAGE_ADD_NOTE_SUCCESS, + Messages.formatPerson(editedPerson))); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof NoteCommand)) { + return false; + } + + NoteCommand e = (NoteCommand) other; + return name.equals(e.name) + && note.equals(e.note); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("name", name) + .add("note", note) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/PinCommand.java b/src/main/java/seedu/address/logic/commands/PinCommand.java new file mode 100644 index 00000000000..6a5594cf9b0 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/PinCommand.java @@ -0,0 +1,73 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; + +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.Messages; +import seedu.address.logic.messages.PinMessages; +import seedu.address.model.Model; +import seedu.address.model.person.Name; +import seedu.address.model.person.Person; + +//@@author yleeyilin +/** + * Pins a contact in PoochPlanner. + */ +public class PinCommand extends Command { + public static final String COMMAND_WORD = "/pin"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Pins a contact to the address book. " + + "Parameters: " + + PREFIX_NAME + "NAME " + + "\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe other "; + public static final String MESSAGE_NULL_NAME = "Specified name to pin contact is null."; + public static final String LOGGER_EXECUTE_PIN_MESSAGE = "Started executing the pin command."; + + private final Name name; + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Constructs a PinCommand object. + * @param name Name of the person in PoochPlanner to pin. + */ + public PinCommand(Name name) { + requireNonNull(name); + this.name = name; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + assert (name != null) : MESSAGE_NULL_NAME; + logger.info(LOGGER_EXECUTE_PIN_MESSAGE); + requireNonNull(model); + + Person personToPin = model.findByName(name, PinMessages.MESSAGE_PIN_NAME_NOT_FOUND); + Person pinnedPerson = personToPin.updateToPinned(); + + model.setPerson(personToPin, pinnedPerson); + model.updatePinnedPersonList(); + + return new CommandResult(String.format(PinMessages.MESSAGE_PIN_PERSON_SUCCESS, + Messages.formatPerson(personToPin))); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof PinCommand)) { + return false; + } + + PinCommand otherCommand = (PinCommand) other; + return this.name.equals(otherCommand.name); + } +} diff --git a/src/main/java/seedu/address/logic/commands/RateCommand.java b/src/main/java/seedu/address/logic/commands/RateCommand.java new file mode 100644 index 00000000000..66f4013a5bf --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/RateCommand.java @@ -0,0 +1,78 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.Messages; +import seedu.address.logic.messages.RateMessages; +import seedu.address.model.Model; +import seedu.address.model.person.Name; +import seedu.address.model.person.Person; +import seedu.address.model.person.Rating; + +//@@author jamessinmaojun +/** + * Rates a person identified using their displayed name from the address book. + */ +public class RateCommand extends Command { + public static final String COMMAND_WORD = "/rate"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Rates the person identified by their name.\n" + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_RATING + "RATING " + + "Example: " + COMMAND_WORD + " " + PREFIX_NAME + "Moochie " + PREFIX_RATING + "2"; + + private final Name targetName; + private final Rating rating; + + /** + * Creates a RateCommand object. + * @param name Name of the person in the person list to rate. + */ + public RateCommand(Name name, Rating rating) { + this.targetName = name; + this.rating = rating; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + Person personToRate = model.findByName(targetName, RateMessages.MESSAGE_RATE_NAME_NOT_FOUND); + + Person ratedPerson = personToRate.updateRating(rating); + + model.setPerson(personToRate, ratedPerson); + model.updateFilteredPersonListWithCommit(PREDICATE_SHOW_ALL_PERSONS); + + return new CommandResult(String.format(RateMessages.MESSAGE_RATE_PERSON_SUCCESS, + Messages.formatPerson(personToRate))); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof RateCommand)) { + return false; + } + + RateCommand otherRateCommand = (RateCommand) other; + return targetName.equals(otherRateCommand.targetName); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("targetName", targetName) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/RedoCommand.java b/src/main/java/seedu/address/logic/commands/RedoCommand.java new file mode 100644 index 00000000000..32a16d4fcfd --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/RedoCommand.java @@ -0,0 +1,59 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.RedoMessages; +import seedu.address.model.Model; + +//@@author chiageng +/** + * Redoes the last undid command. + */ +public class RedoCommand extends Command { + public static final String COMMAND_WORD = "/redo"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Redo previous undid command. "; + public static final String LOGGER_EXECUTE_REDO_MESSAGE = "Going to execute redo command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Creates a RedoCommand Object. + */ + public RedoCommand() { + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.log(Level.INFO, LOGGER_EXECUTE_REDO_MESSAGE); + requireNonNull(model); + + if (!model.canRedoAddressBook()) { + throw new CommandException(RedoMessages.MESSAGE_REDO_FAIL); + } + + model.redoAddressBook(); + return new CommandResult(RedoMessages.MESSAGE_REDO_SUCCESS); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + return other instanceof RedoCommand; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/RemindCommand.java b/src/main/java/seedu/address/logic/commands/RemindCommand.java new file mode 100644 index 00000000000..395749e07b6 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/RemindCommand.java @@ -0,0 +1,50 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.messages.RemindMessages; +import seedu.address.model.Model; +import seedu.address.model.person.RemindPredicate; + +//@@author jannaleong +/** + * Displays all persons with notes that have deadlines from + * the current day onwards. + */ +public class RemindCommand extends Command { + public static final String COMMAND_WORD = "/remind"; + public static final String LOGGER_EXECUTE_REMIND_MESSAGE = "Started executing the remind command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + @Override + public CommandResult execute(Model model) { + logger.info(LOGGER_EXECUTE_REMIND_MESSAGE); + requireNonNull(model); + + model.updateFilteredPersonList(new RemindPredicate()); + + return new CommandResult( + String.format(RemindMessages.MESSAGE_REMIND_PERSON_SUCCESS, model.getFilteredPersonList().size())); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + return other instanceof RemindCommand; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/SearchCommand.java b/src/main/java/seedu/address/logic/commands/SearchCommand.java new file mode 100644 index 00000000000..8be48f4a89a --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SearchCommand.java @@ -0,0 +1,76 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.messages.SearchMessages; +import seedu.address.model.Model; +import seedu.address.model.person.KeywordPredicate; + +//@@author Joshy837 +/** + * Finds and lists all persons in address book whose name contains any of the argument keywords. + * Keyword matching is case-insensitive. + */ +public class SearchCommand extends Command { + public static final String COMMAND_WORD = "/search"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Searches all persons whose names contain any of " + + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: KEYWORD \n" + + "Example: " + COMMAND_WORD + + "/search ; name : [full/partial name]\n" + + "/search ; phone : [full/partial phone]\n" + + "/search ; address : [full/partial address]\n" + + "/search ; email : [full/partial email]\n" + + "/search ; product : [full/partial product name]\n" + + "/search ; employment : [employment]"; + private static final Logger logger = LogsCenter.getLogger(SearchCommand.class); + + private final KeywordPredicate predicate; + + /** + * Creates a SearchCommand object. + * @param predicate The predicate to filter the address book by. + */ + public SearchCommand(KeywordPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + + model.updateFilteredPersonList(predicate); + + logger.fine(String.format(SearchMessages.MESSAGE_SEARCH_PERSON_SUCCESS, + model.getFilteredPersonList().size())); + + return new CommandResult( + String.format(SearchMessages.MESSAGE_SEARCH_PERSON_SUCCESS, model.getFilteredPersonList().size())); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof SearchCommand)) { + return false; + } + + SearchCommand otherSearchCommand = (SearchCommand) other; + return predicate.equals(otherSearchCommand.predicate); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("predicate", predicate) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/SortCommand.java b/src/main/java/seedu/address/logic/commands/SortCommand.java new file mode 100644 index 00000000000..710520e5009 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SortCommand.java @@ -0,0 +1,72 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.SortMessages; +import seedu.address.logic.parser.Prefix; +import seedu.address.model.Model; + +//@@author Joshy837 +/** + * Format full help instructions for every command for display. + */ +public class SortCommand extends Command { + public static final String COMMAND_WORD = "/sort"; + public static final String MESSAGE_USAGE = + "Sorts the address book based on specified parameters.\n" + + "Example: /sort ; field : name"; + public static final String MESSAGE_CONSTRAINTS = + "Only accepts name, phone, email, address, " + + "salary, employment, price, product, " + + "skill, commission, tag, note, pin " + + "and rating as valid command type inputs."; + private static final Logger logger = LogsCenter.getLogger(SortCommand.class); + + private final Prefix prefix; + + /** + * Creates a SortCommand object. + * @param prefix The contact field that the user wants to sort the address book by. + */ + public SortCommand(Prefix prefix) { + requireNonNull(prefix); + this.prefix = prefix; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + model.updateSortedPersonList(prefix); + + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + + if (prefix.getPrefix().equalsIgnoreCase("phone")) { + logger.fine(String.format(SortMessages.MESSAGE_SORT_PERSON_SUCCESS, "phone number")); + return new CommandResult(String.format(SortMessages.MESSAGE_SORT_PERSON_SUCCESS, "phone number")); + } else { + logger.fine(String.format(SortMessages.MESSAGE_SORT_PERSON_SUCCESS, prefix.getPrefix())); + return new CommandResult(String.format(SortMessages.MESSAGE_SORT_PERSON_SUCCESS, prefix.getPrefix())); + } + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof SortCommand)) { + return false; + } + + SortCommand otherSearchCommand = (SortCommand) other; + return prefix.equals(otherSearchCommand.prefix); + } +} diff --git a/src/main/java/seedu/address/logic/commands/UndoCommand.java b/src/main/java/seedu/address/logic/commands/UndoCommand.java new file mode 100644 index 00000000000..865071e8513 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/UndoCommand.java @@ -0,0 +1,59 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.UndoMessages; +import seedu.address.model.Model; + +//@@author chiageng +/** + * Undoes the last executed command. + */ +public class UndoCommand extends Command { + public static final String COMMAND_WORD = "/undo"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Undo previous command. "; + public static final String LOGGER_EXECUTE_UNDO_MESSAGE = "Going to execute undo command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Creates an UndoCommand object. + */ + public UndoCommand() { + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.log(Level.INFO, LOGGER_EXECUTE_UNDO_MESSAGE); + requireNonNull(model); + + if (!model.canUndoAddressBook()) { + throw new CommandException(UndoMessages.MESSAGE_UNDO_FAIL); + } + + model.undoAddressBook(); + return new CommandResult(UndoMessages.MESSAGE_UNDO_SUCCESS); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + return other instanceof UndoCommand; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/UnpinCommand.java b/src/main/java/seedu/address/logic/commands/UnpinCommand.java new file mode 100644 index 00000000000..d98862c9c37 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/UnpinCommand.java @@ -0,0 +1,73 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; + +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.Messages; +import seedu.address.logic.messages.UnpinMessages; +import seedu.address.model.Model; +import seedu.address.model.person.Name; +import seedu.address.model.person.Person; + +//@@author yleeyilin +/** + * Unpins a contact in PoochPlanner. + */ +public class UnpinCommand extends Command { + public static final String COMMAND_WORD = "/unpin"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Unpins a contact from the address book. " + + "Parameters: " + + PREFIX_NAME + "NAME " + + "\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe other "; + public static final String MESSAGE_NULL_NAME = "Specified name to unpin contact is null."; + public static final String LOGGER_EXECUTE_UNPIN_MESSAGE = "Started executing the unpin command."; + + private final Name name; + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Constructs an UnpinCommand object. + * @param name Name of the person in PoochPlanner to unpin. + */ + public UnpinCommand(Name name) { + requireNonNull(name); + this.name = name; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + assert (name != null) : MESSAGE_NULL_NAME; + logger.info(LOGGER_EXECUTE_UNPIN_MESSAGE); + requireNonNull(model); + + Person personToUnpin = model.findByName(name, UnpinMessages.MESSAGE_UNPIN_NAME_NOT_FOUND); + Person unpinnedPerson = personToUnpin.updateToUnpinned(); + + model.setPerson(personToUnpin, unpinnedPerson); + model.updatePinnedPersonList(); + + return new CommandResult(String.format(UnpinMessages.MESSAGE_UNPIN_PERSON_SUCCESS, + Messages.formatPerson(personToUnpin))); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof UnpinCommand)) { + return false; + } + + UnpinCommand otherCommand = (UnpinCommand) other; + return this.name.equals(otherCommand.name); + } +} diff --git a/src/main/java/seedu/address/logic/messages/AddMessages.java b/src/main/java/seedu/address/logic/messages/AddMessages.java new file mode 100644 index 00000000000..395a4cdd04b --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/AddMessages.java @@ -0,0 +1,24 @@ +package seedu.address.logic.messages; + +//@@author chiageng +/** + * Container for {@code AddCommand} visible messages. + */ +public class AddMessages extends Messages { + public static final String ADD = "add"; + public static final String ADD_MAINTAINER = "add-maintainer"; + public static final String ADD_STAFF = "add-staff"; + public static final String ADD_SUPPLIER = "add-supplier"; + public static final String OTHER_TYPE = "other"; + public static final String STAFF_TYPE = "staff"; + public static final String SUPPLIER_TYPE = "supplier"; + public static final String MAINTAINER_TYPE = "maintainer"; + public static final String FAILED_TO_ADD = "Failed to add Pooch Contact - "; + public static final String MESSAGE_ADD_PERSON_SUCCESS = "Woof! Added %1$s successfully! \uD83D\uDC36"; + public static final String MESSAGE_ADD_DUPLICATE_PERSON = FAILED_TO_ADD + + "Pooch Contact already exists in the planner \uD83D\uDC3E"; + public static final String MESSAGE_ADD_INVALID_PARAMETERS = FAILED_TO_ADD + + "%1$s \uD83D\uDC3E"; + + +} diff --git a/src/main/java/seedu/address/logic/messages/ClearMessages.java b/src/main/java/seedu/address/logic/messages/ClearMessages.java new file mode 100644 index 00000000000..80b273be9c8 --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/ClearMessages.java @@ -0,0 +1,10 @@ +package seedu.address.logic.messages; + +//@@author jamessinmaojun +/** + * Container for {@code ClearCommand} visible messages. + */ +public class ClearMessages extends Messages { + public static final String MESSAGE_CLEAR_POOCHPLANNER_SUCCESS = + "Woof! Cleared PoochPlanner successfully! \uD83D\uDC36"; +} diff --git a/src/main/java/seedu/address/logic/messages/DeleteMessages.java b/src/main/java/seedu/address/logic/messages/DeleteMessages.java new file mode 100644 index 00000000000..a31df2284a9 --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/DeleteMessages.java @@ -0,0 +1,17 @@ +package seedu.address.logic.messages; + +//@@author jamessinmaojun +/** + * Container for {@code DeleteCommand} visible messages. + */ +public class DeleteMessages extends Messages { + public static final String DELETE = "delete"; + public static final String FAILED_TO_DELETE = "Failed to delete Pooch Contact - "; + public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Woof! Deleted %1$s successfully! \uD83D\uDC36"; + public static final String MESSAGE_DELETE_NAME_NOT_FOUND = FAILED_TO_DELETE + + "Name does not exist in your address book.\uD83D\uDC3E"; + public static final String MESSAGE_DELETE_MISSING_NAME = FAILED_TO_DELETE + + "Delete requires a name field. %1$s\uD83D\uDC3E"; + public static final String MESSAGE_DELETE_INVALID_PARAMETERS = FAILED_TO_DELETE + + "%1$s \uD83D\uDC3E"; +} diff --git a/src/main/java/seedu/address/logic/messages/EditMessages.java b/src/main/java/seedu/address/logic/messages/EditMessages.java new file mode 100644 index 00000000000..14f24d42ad5 --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/EditMessages.java @@ -0,0 +1,28 @@ +package seedu.address.logic.messages; + +/** + * Container for {@code EditCommand} visible messages. + */ +public class EditMessages extends Messages { + public static final String EDIT = "edit"; + public static final String EDIT_STAFF = "edit-staff"; + public static final String EDIT_MAINTAINER = "edit-maintainer"; + public static final String EDIT_SUPPLIER = "edit-supplier"; + public static final String FAILED_TO_EDIT = "Failed to edit Pooch Contact - "; + public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Woof! Edited %1$s successfully! \uD83D\uDC36"; + public static final String MESSAGE_EDIT_EMPTY_FIELD = FAILED_TO_EDIT + + "Field is empty or invalid! \uD83D\uDC3E"; + public static final String MESSAGE_EDIT_INVALID_NAME = FAILED_TO_EDIT + "%1$s \uD83D\uDC3E"; + public static final String MESSAGE_MULTIPLE_NAME = FAILED_TO_EDIT + + "Entering multiple name prefixes is not allowed. \n %1$s \uD83D\uDC3E"; + public static final String MESSAGE_EDIT_INVALID_FIELD = FAILED_TO_EDIT + + "%1$s \uD83D\uDC3E"; + public static final String MESSAGE_INVALID_EDIT_PERSON = "Name does not exist in our address book \uD83D\uDC3E" + + "Make sure that you are attempting to edit OTHER."; + public static final String MESSAGE_INVALID_EDIT_STAFF = "Name does not exist in our address book \uD83D\uDC3E" + + "Make sure that you are attempting to edit STAFF."; + public static final String MESSAGE_INVALID_EDIT_MAINTAINER = "Name does not exist in our address book \uD83D\uDC3E" + + " Make sure that you are attempting to edit MAINTAINER."; + public static final String MESSAGE_INVALID_EDIT_SUPPLIER = "Name does not exist in our address book \uD83D\uDC3E" + + " Make sure that you are attempting to edit SUPPLIER."; +} diff --git a/src/main/java/seedu/address/logic/messages/HelpMessages.java b/src/main/java/seedu/address/logic/messages/HelpMessages.java new file mode 100644 index 00000000000..e59bce434e7 --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/HelpMessages.java @@ -0,0 +1,155 @@ +package seedu.address.logic.messages; + +//@@author jannaleong +/** + * Container for {@code HelpCommand} visible messages. + */ +public class HelpMessages extends Messages { + public static final String HELP = "help"; + public static final String FAILED_TO_HELP = "Failed to display help window - "; + public static final String MESSAGES_SHOWING_HELP_MESSAGE = "Opened help window."; + public static final String MESSAGES_SHOWING_DELETE_HELP_MESSAGE = "Opened help window for delete command."; + public static final String MESSAGES_SHOWING_EDIT_HELP_MESSAGE = "Opened help window for edit command."; + public static final String MESSAGES_SHOWING_ADD_HELP_MESSAGE = + "Opened help window for add command."; + public static final String MESSAGES_SHOWING_SEARCH_HELP_MESSAGE = + "Opened help window for search command."; + public static final String MESSAGES_SHOWING_EXIT_HELP_MESSAGE = + "Opened help window for exit command."; + public static final String MESSAGES_SHOWING_LIST_HELP_MESSAGE = + "Opened help window for list command."; + public static final String MESSAGES_SHOWING_NOTE_HELP_MESSAGE = + "Opened help window for note command."; + public static final String MESSAGES_SHOWING_PIN_HELP_MESSAGE = + "Opened help window for pin command."; + public static final String MESSAGES_SHOWING_UNPIN_HELP_MESSAGE = + "Opened help window for unpin command."; + public static final String MESSAGES_SHOWING_RATE_HELP_MESSAGE = + "Opened help window for rate command."; + public static final String MESSAGES_SHOWING_REDO_HELP_MESSAGE = + "Opened help window for redo command."; + public static final String MESSAGES_SHOWING_UNDO_HELP_MESSAGE = + "Opened help window for undo command."; + public static final String MESSAGES_SHOWING_REMIND_HELP_MESSAGE = + "Opened help window for remind command."; + public static final String MESSAGES_SHOWING_SORT_HELP_MESSAGE = + "Opened help window for sort command."; + public static final String MESSAGES_SHOWING_CLEAR_HELP_MESSAGE = + "Opened help window for clear command."; + public static final String MESSAGES_INVALID_COMMAND_TYPE = "Invalid command type given."; + public static final String MESSAGE_HELP_MISSING_COMMAND = "Failed to give help - " + + "Help requires a command type field. %1$s\uD83D\uDC3E"; + public static final String MESSAGE_HELP_INVALID_PARAMETERS = FAILED_TO_HELP + + "%1$s \uD83D\uDC3E"; + + public static final String USERGUIDE_URL = "https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html"; + + public static final String DISPLAYED_DELETE_MESSAGE = "Deletes the specified contact." + + "\n" + "" + "\n" + + "Format: /delete ; name : [name] " + + "\n" + "" + "\n" + + "Go to our UG for more information : " + USERGUIDE_URL; + + public static final String DISPLAYED_ADD_MESSAGE = "Adds a other/maintainer/supplier/staff person." + + "\n" + "" + "\n" + + "Format:" + "\n" + + "/add-person ; name : [name] ; phone : [phone] ; address : [address] ; email : [email]" + + "\n" + + "/add-maintainer ; name : [name] ; phone : [phone] ; address : [address] ;" + + " email : [email] ; skill : [skill] ; commission : [commission/hr]" + "\n" + + "/add-supplier ; name : [name] ; phone : [phone] ; address : [address] ; email : [email] ;" + + " product : [product] ; price : [price/(quantity)]" + "\n" + + "/add-staff ; name : [name] ; phone : [phone] ; address : [address] ;" + + " email : [email] ; salary : [salary/hr] ; employment : [part-time/full-time]" + "\n" + + "\n" + "" + + "Go to our UG for more information : " + USERGUIDE_URL; + + public static final String DISPLAYED_EDIT_MESSAGE = "Edit the fields of the specified contact" + + "\n" + "" + "\n" + + "Format:" + "\n" + + "/edit-person ; name : [name] ; field : { [field] : [value] }" + "\n" + + "/edit-staff ; name : [name] ; field : { [field] : [value] }" + "\n" + + "/edit-supplier ; name : [name] ; field : { [field] : [value] }" + "\n" + + "/edit-maintainer ; name : [name] ; field : { [field] : [value] }" + "\n" + + "\n" + + "Go to our UG for more information : " + USERGUIDE_URL; + + + public static final String DISPLAYED_SEARCH_MESSAGE = "Searches for a contact using" + + " specified fields and keyword." + + "\n" + "" + "\n" + + "Format:" + "\n" + + "/search ; [target-field] : [value]" + "\n" + + "\n" + + "Go to our UG for more information : " + USERGUIDE_URL; + + public static final String DISPLAYED_EXIT_MESSAGE = "Exits the program." + + "\n" + "" + "\n" + + "Format: " + "/exit" + + "\n" + "" + "\n" + + "Go to our UG for more information : " + USERGUIDE_URL; + + public static final String DISPLAYED_LIST_MESSAGE = "List all contacts." + + "\n" + "" + "\n" + + "Format: " + "/list" + + "\n" + "" + "\n" + + "Go to our UG for more information : " + USERGUIDE_URL; + + public static final String DISPLAYED_NOTE_MESSAGE = "Adds a note to a contact." + + "\n" + "" + "\n" + + "Format:" + "\n" + + "/note ; name : [name] ; note : [note message]" + "\n" + + "OR" + "\n" + + "/note ; name : [name] ; note : [note message] ; deadline : [deadline date]" + + "\n" + "" + "\n" + + "Go to our UG for more information : " + USERGUIDE_URL; + + public static final String DISPLAYED_PIN_MESSAGE = "Pin specified contact at the top of the planner." + + "\n" + "" + "\n" + + "Format: " + "/pin ; name : [name]" + + "\n" + "" + "\n" + + "Go to our UG for more information : " + USERGUIDE_URL; + + public static final String DISPLAYED_UNPIN_MESSAGE = "Unpin specified contact at the top of the planner." + + "\n" + "" + "\n" + + "Format: " + "/unpin ; name : [name]" + + "\n" + "" + "\n" + + "Go to our UG for more information : " + USERGUIDE_URL; + + public static final String DISPLAYED_RATE_MESSAGE = "Adds a rating to a specified contact from 1-5." + + "\n" + "" + "\n" + + "Format: " + "/rate ; name : [name] ; rating : [rating value from 1-5]" + + "\n" + "" + "\n" + + "Go to our UG for more information : " + USERGUIDE_URL; + + public static final String DISPLAYED_REDO_MESSAGE = "Redoes your previous command." + + "\n" + "" + "\n" + + "Format: " + "/redo" + + "\n" + "" + "\n" + + "Go to our UG for more information : " + USERGUIDE_URL; + + public static final String DISPLAYED_UNDO_MESSAGE = "Undoes your previous command." + + "\n" + "" + "\n" + + "Format: " + "/undo" + + "\n" + "" + "\n" + + "Go to our UG for more information : " + USERGUIDE_URL; + + public static final String DISPLAYED_REMIND_MESSAGE = "Displays all contacts with note deadlines" + + " from today onwards." + + "\n" + "" + "\n" + + "Format: " + "/remind" + + "\n" + "" + "\n" + + "Go to our UG for more information : " + USERGUIDE_URL; + + public static final String DISPLAYED_SORT_MESSAGE = "Sorts contacts based on specified field." + + "\n" + "" + "\n" + + "Format: " + "/sort ; [target-field] : [value]" + + "\n" + "" + "\n" + + "Go to our UG for more information : " + USERGUIDE_URL; + + public static final String DISPLAYED_CLEAR_MESSAGE = "Clear all contacts." + + "\n" + "" + "\n" + + "Format: " + "/clear" + + "\n" + "" + "\n" + + "Go to our UG for more information : " + USERGUIDE_URL; +} diff --git a/src/main/java/seedu/address/logic/messages/ListMessages.java b/src/main/java/seedu/address/logic/messages/ListMessages.java new file mode 100644 index 00000000000..ce83748097a --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/ListMessages.java @@ -0,0 +1,10 @@ +package seedu.address.logic.messages; + +//@@author jamessinmaojun +/** + * Container for {@code ListCommand} visible messages. + */ +public class ListMessages extends Messages { + public static final String MESSAGE_LIST_CONTACTS_SUCCESS = "Woof! Listed all contacts! \uD83D\uDC36"; + +} diff --git a/src/main/java/seedu/address/logic/messages/Messages.java b/src/main/java/seedu/address/logic/messages/Messages.java new file mode 100644 index 00000000000..c1da0d3bbb6 --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/Messages.java @@ -0,0 +1,96 @@ +package seedu.address.logic.messages; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import seedu.address.logic.parser.Prefix; +import seedu.address.model.person.Maintainer; +import seedu.address.model.person.Person; +import seedu.address.model.person.Staff; +import seedu.address.model.person.Supplier; + +/** + * Container for general visible messages. + */ +public class Messages { + + public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command. \nPlease refer to the following URL for " + + "the User Guide to obtain further assistance and information.\n" + + "https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html"; + public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; + public static final String MESSAGE_UNKNOWN_FIELD_FORMAT = "Unknown field detected : %1$s \uD83D\uDC3E"; + public static final String MESSAGE_MISSING_FIELD_FORMAT = "Missing field detected : %1$s \uD83D\uDC3E"; + public static final String MESSAGE_COMMAND_FORMAT = "Follow this command format! \n%1$s"; + public static final String MESSAGE_DUPLICATE_FIELDS = + "Multiple values specified for the following single-valued field(s): "; + public static final String MESSAGE_EMPTY_FIELDS = + "No values specified for the following single-valued field(s): "; + + /** + * Returns an error message indicating the duplicate prefixes. + */ + public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePrefixes) { + assert duplicatePrefixes.length > 0; + + Set duplicateFields = + Stream.of(duplicatePrefixes).map(Prefix::toString).collect(Collectors.toSet()); + + return MESSAGE_DUPLICATE_FIELDS + String.join(" ", duplicateFields); + } + + //@@author chiageng + /** + * Formats the {@code Person} including basic person fields for display to the user. + * @param person The person to be formatted. + */ + public static String format(Person person) { + final StringBuilder builder = new StringBuilder(); + builder.append(person.getName()) + .append("; Phone: ") + .append(person.getPhone()) + .append("; Email: ") + .append(person.getEmail()) + .append("; Address: ") + .append(person.getAddress()) + .append("; Tags: "); + person.getTags().forEach(builder::append); + return builder.toString(); + } + //@@author + + //@@author chiageng + /** + * Formats the {@code Person} for display to the user. + * @param person The person to be formatted. + */ + public static String formatPerson(Person person) { + final StringBuilder builder = new StringBuilder(); + if (person instanceof Staff) { + builder.append("Staff "); + } else if (person instanceof Supplier) { + builder.append("Supplier "); + } else if (person instanceof Maintainer) { + builder.append("Maintainer "); + } else { + builder.append("General Contact "); + } + builder.append(person.getName()); + return builder.toString(); + } + //@@author + + //@@author Joshy837 + /** + * Returns an error message indicating the prefixes with no values. + */ + public static String getErrorMessageForEmptyPrefixes(Prefix... emptyPrefixes) { + assert emptyPrefixes.length > 0; + + Set emptyFields = + Stream.of(emptyPrefixes).map(Prefix::getTrimmedPrefix).collect(Collectors.toSet()); + + return MESSAGE_EMPTY_FIELDS + "[" + String.join(", ", emptyFields) + "]"; + } + //@@author +} diff --git a/src/main/java/seedu/address/logic/messages/NoteMessages.java b/src/main/java/seedu/address/logic/messages/NoteMessages.java new file mode 100644 index 00000000000..65d204b781e --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/NoteMessages.java @@ -0,0 +1,19 @@ +package seedu.address.logic.messages; + +//@@author jannaleong +/** + * Container for {@code NoteCommand} visible messages. + */ +public class NoteMessages extends Messages { + public static final String NOTE = "note"; + public static final String DEFAULT_NOTE = "No note here"; + public static final String FAILED_TO_ADD_NOTE = "Failed to add note to Pooch Contact - "; + public static final String MESSAGE_ADD_NOTE_SUCCESS = + "Woof! Added note to %1$s successfully! \uD83D\uDC36"; + public static final String MESSAGE_NOTE_NAME_NOT_FOUND = FAILED_TO_ADD_NOTE + + "Name does not exist in our address book. \uD83D\uDC3E"; + public static final String MESSAGE_DEADLINE_NOT_SPECIFIED = FAILED_TO_ADD_NOTE + + "Deadline is not specified. \uD83D\uDC3E"; + public static final String MESSAGE_NOTE_INVALID_PARAMETERS = FAILED_TO_ADD_NOTE + + "%1$s \uD83D\uDC3E"; +} diff --git a/src/main/java/seedu/address/logic/messages/PinMessages.java b/src/main/java/seedu/address/logic/messages/PinMessages.java new file mode 100644 index 00000000000..6be8dc44eb8 --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/PinMessages.java @@ -0,0 +1,13 @@ +package seedu.address.logic.messages; + +/** + * Container for {@code PinCommand} visible messages. + */ +public class PinMessages extends Messages { + public static final String PIN = "pin"; + public static final String FAILED_TO_PIN = "Failed to pin Pooch Contact - "; + public static final String MESSAGE_PIN_PERSON_SUCCESS = "Woof! Pinned %1$s successfully! \uD83D\uDC36"; + public static final String MESSAGE_PIN_INVALID_NAME = " %1$s \uD83D\uDC3E"; + public static final String MESSAGE_PIN_NAME_NOT_FOUND = FAILED_TO_PIN + + "Name does not exist in our address book \uD83D\uDC3E"; +} diff --git a/src/main/java/seedu/address/logic/messages/RateMessages.java b/src/main/java/seedu/address/logic/messages/RateMessages.java new file mode 100644 index 00000000000..1b6f0ce6760 --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/RateMessages.java @@ -0,0 +1,20 @@ +package seedu.address.logic.messages; + +//@@author jamessinmaojun +/** + * Container for {@code RateCommand} visible messages. + */ +public class RateMessages extends Messages { + public static final String RATE = "rate"; + public static final String DEFAULT_RATING = "0"; + public static final String FAILED_TO_RATE = "Failed to rate Pooch Contact - "; + public static final String MESSAGE_RATE_PERSON_SUCCESS = "Woof! Rated %1$s successfully! \uD83D\uDC36"; + public static final String MESSAGE_RATE_NAME_NOT_FOUND = FAILED_TO_RATE + + "Name does not exist in our address book.\uD83D\uDC3E"; + public static final String MESSAGE_RATE_MISSING_NAME = FAILED_TO_RATE + + "Rate requires a name field. %1$s\uD83D\uDC3E"; + public static final String MESSAGE_RATE_MISSING_RATING = FAILED_TO_RATE + + "Please enter a rating. %1$s\uD83D\uDC3E"; + public static final String MESSAGE_RATE_INVALID_PARAMETERS = FAILED_TO_RATE + + "%1$s \uD83D\uDC3E"; +} diff --git a/src/main/java/seedu/address/logic/messages/RedoMessages.java b/src/main/java/seedu/address/logic/messages/RedoMessages.java new file mode 100644 index 00000000000..896eaf45e30 --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/RedoMessages.java @@ -0,0 +1,10 @@ +package seedu.address.logic.messages; + +//@@author chiageng +/** + * Container for {@code RedoCommand} visible messages. + */ +public class RedoMessages extends Messages { + public static final String MESSAGE_REDO_SUCCESS = "Woof! Redo successfully! \uD83D\uDC36"; + public static final String MESSAGE_REDO_FAIL = "Woof! There are no more commands to redo!"; +} diff --git a/src/main/java/seedu/address/logic/messages/RemindMessages.java b/src/main/java/seedu/address/logic/messages/RemindMessages.java new file mode 100644 index 00000000000..a4566880a3e --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/RemindMessages.java @@ -0,0 +1,9 @@ +package seedu.address.logic.messages; + +//@@author jannaleong +/** + * Container for {@code RemindCommand} visible messages. + */ +public class RemindMessages extends Messages { + public static final String MESSAGE_REMIND_PERSON_SUCCESS = "Woof! %1$s contact(s) found! \uD83D\uDC36"; +} diff --git a/src/main/java/seedu/address/logic/messages/SearchMessages.java b/src/main/java/seedu/address/logic/messages/SearchMessages.java new file mode 100644 index 00000000000..ca75a8f68f0 --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/SearchMessages.java @@ -0,0 +1,15 @@ +package seedu.address.logic.messages; + +//@@author Joshy837 +/** + * Container for {@code SearchCommand} visible messages. + */ +public class SearchMessages extends Messages { + public static final String SEARCH = "search"; + public static final String FAILED_TO_SEARCH = "Failed to search Pooch Contact - "; + public static final String MESSAGE_SEARCH_PERSON_SUCCESS = "Woof! %1$s contact(s) found! \uD83D\uDC36"; + public static final String MESSAGE_SEARCH_MISSING_FIELD = FAILED_TO_SEARCH + + "Search requires a name / phone / address / email / product / employment field. \uD83D\uDC3E"; + public static final String MESSAGE_SEARCH_INVALID_FIELD = FAILED_TO_SEARCH + + "PoochPlanner doesn't recognise the field \uD83D\uDC3E"; +} diff --git a/src/main/java/seedu/address/logic/messages/SortMessages.java b/src/main/java/seedu/address/logic/messages/SortMessages.java new file mode 100644 index 00000000000..2ca432f3f89 --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/SortMessages.java @@ -0,0 +1,19 @@ +package seedu.address.logic.messages; + +//@@author Joshy837 +/** + * Container for {@code SortCommand} visible messages. + */ +public class SortMessages extends Messages { + public static final String SORT = "sort"; + public static final String FAILED_TO_SORT = "Failed to sort Pooch Contact - "; + public static final String MESSAGE_SORT_PERSON_SUCCESS = + "Woof! Sorted PoochPlanner by %1$s successfully! \uD83D\uDC36"; + public static final String MESSAGE_SORT_INVALID_FIELD = + FAILED_TO_SORT + "Please input a valid target field! \uD83D\uDC3E"; + public static final String MESSAGE_SORT_MISSING_FIELD = + FAILED_TO_SORT + "Missing field detected : [field] \uD83D\uDC3E\n" + + "Follow this command format! \n" + + "Sorts the address book based on specified parameters.\n" + + "Example: /sort ; field : name"; +} diff --git a/src/main/java/seedu/address/logic/messages/UndoMessages.java b/src/main/java/seedu/address/logic/messages/UndoMessages.java new file mode 100644 index 00000000000..76987e19a4f --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/UndoMessages.java @@ -0,0 +1,10 @@ +package seedu.address.logic.messages; + +//@@author chiageng +/** + * Container for {@code UndoCommand} visible messages. + */ +public class UndoMessages extends Messages { + public static final String MESSAGE_UNDO_SUCCESS = "Woof! Undo successfully! \uD83D\uDC36"; + public static final String MESSAGE_UNDO_FAIL = "Woof! There are no more commands to undo!"; +} diff --git a/src/main/java/seedu/address/logic/messages/UnpinMessages.java b/src/main/java/seedu/address/logic/messages/UnpinMessages.java new file mode 100644 index 00000000000..425c3aa5e39 --- /dev/null +++ b/src/main/java/seedu/address/logic/messages/UnpinMessages.java @@ -0,0 +1,13 @@ +package seedu.address.logic.messages; + +/** + * Container for {@code UnpinCommand} visible messages. + */ +public class UnpinMessages extends Messages { + public static final String UNPIN = "unpin"; + public static final String FAILED_TO_UNPIN = "Failed to unpin Pooch Contact - "; + public static final String MESSAGE_UNPIN_PERSON_SUCCESS = "Woof! Unpinned %1$s successfully! \uD83D\uDC36"; + public static final String MESSAGE_UNPIN_INVALID_NAME = "Failed to unpin Pooch Contact - %1$s \uD83D\uDC3E"; + public static final String MESSAGE_UNPIN_NAME_NOT_FOUND = FAILED_TO_UNPIN + + "Name does not exist in your address book \uD83D\uDC3E"; +} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java index 4ff1a97ed77..973e1c3938e 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java @@ -1,61 +1,101 @@ package seedu.address.logic.parser; -import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.messages.AddMessages.ADD; +import static seedu.address.logic.messages.AddMessages.FAILED_TO_ADD; +import static seedu.address.logic.messages.AddMessages.OTHER_TYPE; +import static seedu.address.logic.messages.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.messages.NoteMessages.DEFAULT_NOTE; +import static seedu.address.logic.messages.RateMessages.DEFAULT_RATING; import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; 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_NOTE; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING; +import java.util.HashSet; import java.util.Set; -import java.util.stream.Stream; +import java.util.logging.Level; +import java.util.logging.Logger; +import seedu.address.commons.core.LogsCenter; import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.messages.AddMessages; 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.Note; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Rating; import seedu.address.model.tag.Tag; +//@@author chiageng /** - * Parses input arguments and creates a new AddCommand object + * Parses input arguments and creates a new AddCommand object. */ public class AddCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "argument to pass for add command is null"; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for add command."; + private final Logger logger = LogsCenter.getLogger(getClass()); /** * Parses the given {@code String} of arguments in the context of the AddCommand - * and returns an AddCommand object for execution. - * @throws ParseException if the user input does not conform the expected format + * and returns an AddCommand object for execution. Parameter {@code args} cannot be null. + * @param args The arguments of the AddCommand object. + * @throws ParseException If the user input does not conform to the expected format. */ public AddCommand parse(String args) throws ParseException { + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + 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_NOTE, + PREFIX_RATING); - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) - || !argMultimap.getPreamble().isEmpty()) { + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(args, AddCommand.MESSAGE_USAGE, ADD, + FAILED_TO_ADD, + PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_NOTE, PREFIX_RATING); + ParserUtil.verifyNoMissingField(argMultimap, AddCommand.MESSAGE_USAGE, ADD, + FAILED_TO_ADD, + PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL); + boolean isPreambleEmpty = argMultimap.isPreambleEmpty(); + if (!isPreambleEmpty) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); } argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS); - 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()); - Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); - Person person = new Person(name, phone, email, address, tagList); + Person person = createPersonContact(argMultimap); return new AddCommand(person); } + //@@author /** - * Returns true if none of the prefixes contains empty {@code Optional} values in the given - * {@code ArgumentMultimap}. + * Creates a {@code Person} contact based on the argument multimap. + * @param argMultimap Contains the mappings of values to the specific prefixes. + * @return A person contact. + * @throws ParseException If the user enters invalid parameters. */ - private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { - return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + private Person createPersonContact(ArgumentMultimap argMultimap) throws ParseException { + try { + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).orElseThrow()); + Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).orElseThrow()); + Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).orElseThrow()); + Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).orElseThrow()); + String noteContent = argMultimap.getValue(PREFIX_NOTE).orElse(DEFAULT_NOTE); + Note note = noteContent.equals(DEFAULT_NOTE) ? new Note(noteContent) : ParserUtil.parseNote(noteContent); + Rating rating = ParserUtil.parseRating(argMultimap.getValue(PREFIX_RATING).orElse(DEFAULT_RATING)); + Tag tag = new Tag(OTHER_TYPE); + Set tags = new HashSet<>(); + tags.add(tag); + return new Person(name, phone, email, address, note, tags, rating); + } catch (ParseException pe) { + throw new ParseException(String.format(AddMessages.MESSAGE_ADD_INVALID_PARAMETERS, pe.getMessage())); + } } - } diff --git a/src/main/java/seedu/address/logic/parser/AddMaintainerCommandParser.java b/src/main/java/seedu/address/logic/parser/AddMaintainerCommandParser.java new file mode 100644 index 00000000000..ca55776d9e8 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddMaintainerCommandParser.java @@ -0,0 +1,108 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.messages.AddMessages.ADD_MAINTAINER; +import static seedu.address.logic.messages.AddMessages.FAILED_TO_ADD; +import static seedu.address.logic.messages.AddMessages.MAINTAINER_TYPE; +import static seedu.address.logic.messages.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.messages.NoteMessages.DEFAULT_NOTE; +import static seedu.address.logic.messages.RateMessages.DEFAULT_RATING; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMMISSION; +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_NOTE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SKILL; + +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.AddMaintainerCommand; +import seedu.address.logic.messages.AddMessages; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Address; +import seedu.address.model.person.Commission; +import seedu.address.model.person.Email; +import seedu.address.model.person.Maintainer; +import seedu.address.model.person.Name; +import seedu.address.model.person.Note; +import seedu.address.model.person.Phone; +import seedu.address.model.person.Rating; +import seedu.address.model.person.Skill; +import seedu.address.model.tag.Tag; + +//@@author chiageng +/** + * Parses input arguments and creates a new AddMaintainerCommand object. + */ +public class AddMaintainerCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "argument to pass for add maintainer command is null"; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for add maintainer command."; + private final Logger logger = LogsCenter.getLogger(getClass()); + /** + * Parses the given {@code String} of arguments in the context of the AddStaffCommand + * and returns an AddCommand object for execution. Parameter {@code args} cannot be null. + * @param args The arguments of the AddMaintainerCommand object. + * @throws ParseException If the user input does not conform to the expected format. + */ + public AddMaintainerCommand parse(String args) throws ParseException { + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_SKILL, PREFIX_COMMISSION, PREFIX_RATING); + + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(args, AddMaintainerCommand.MESSAGE_USAGE, ADD_MAINTAINER, + FAILED_TO_ADD, + PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_SKILL, PREFIX_COMMISSION, PREFIX_RATING); + ParserUtil.verifyNoMissingField(argMultimap, AddMaintainerCommand.MESSAGE_USAGE, ADD_MAINTAINER, + FAILED_TO_ADD, + PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_SKILL, PREFIX_COMMISSION); + boolean isPreambleEmpty = argMultimap.isPreambleEmpty(); + if (!isPreambleEmpty) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddMaintainerCommand.MESSAGE_USAGE)); + } + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_SKILL, PREFIX_COMMISSION); + + Maintainer person = createMaintainerContact(argMultimap); + + return new AddMaintainerCommand(person); + } + //@@author + + /** + * Creates a {@code Maintainer} contact based on the argument multimap. + * @param argMultimap Contains the mappings of values to the specific prefixes. + * @return A maintainer contact. + * @throws ParseException If the user enters invalid parameters. + */ + private Maintainer createMaintainerContact(ArgumentMultimap argMultimap) throws ParseException { + try { + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).orElseThrow()); + Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).orElseThrow()); + Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).orElseThrow()); + Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).orElseThrow()); + String noteContent = argMultimap.getValue(PREFIX_NOTE).orElse(DEFAULT_NOTE); + Note note = noteContent.equals(DEFAULT_NOTE) ? new Note(noteContent) : ParserUtil.parseNote(noteContent); + Rating rating = ParserUtil.parseRating(argMultimap.getValue(PREFIX_RATING).orElse(DEFAULT_RATING)); + Tag tag = new Tag(MAINTAINER_TYPE); + Set tags = new HashSet<>(); + tags.add(tag); + Skill skill = ParserUtil.parseSkill(argMultimap.getValue(PREFIX_SKILL).orElseThrow()); + Commission commission = ParserUtil.parseCommission(argMultimap.getValue(PREFIX_COMMISSION).orElseThrow()); + return new Maintainer(name, phone, email, address, note, tags, skill, commission, rating); + } catch (ParseException pe) { + throw new ParseException(String.format(AddMessages.MESSAGE_ADD_INVALID_PARAMETERS, pe.getMessage())); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddStaffCommandParser.java b/src/main/java/seedu/address/logic/parser/AddStaffCommandParser.java new file mode 100644 index 00000000000..8f506ebb7a9 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddStaffCommandParser.java @@ -0,0 +1,108 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.messages.AddMessages.ADD_STAFF; +import static seedu.address.logic.messages.AddMessages.FAILED_TO_ADD; +import static seedu.address.logic.messages.AddMessages.STAFF_TYPE; +import static seedu.address.logic.messages.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.messages.NoteMessages.DEFAULT_NOTE; +import static seedu.address.logic.messages.RateMessages.DEFAULT_RATING; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMPLOYMENT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY; + +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.AddStaffCommand; +import seedu.address.logic.messages.AddMessages; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Address; +import seedu.address.model.person.Email; +import seedu.address.model.person.Employment; +import seedu.address.model.person.Name; +import seedu.address.model.person.Note; +import seedu.address.model.person.Phone; +import seedu.address.model.person.Rating; +import seedu.address.model.person.Salary; +import seedu.address.model.person.Staff; +import seedu.address.model.tag.Tag; + +//@@author chiageng +/** + * Parses input arguments and creates a new AddStaffCommand object. + */ +public class AddStaffCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "argument to pass for add staff command is null"; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for add staff command."; + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Parses the given {@code String} of arguments in the context of the AddStaffCommand + * and returns an AddCommand object for execution. Parameter {@code args} cannot be null. + * @throws ParseException If the user input does not conform to the expected format. + */ + public AddStaffCommand parse(String args) throws ParseException { + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_SALARY, PREFIX_EMPLOYMENT, PREFIX_RATING); + + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(args, AddStaffCommand.MESSAGE_USAGE, ADD_STAFF, + FAILED_TO_ADD, + PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_SALARY, PREFIX_EMPLOYMENT, PREFIX_RATING); + ParserUtil.verifyNoMissingField(argMultimap, AddStaffCommand.MESSAGE_USAGE, ADD_STAFF, + FAILED_TO_ADD, + PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_EMPLOYMENT, PREFIX_SALARY); + boolean isPreambleEmpty = argMultimap.isPreambleEmpty(); + if (!isPreambleEmpty) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddStaffCommand.MESSAGE_USAGE)); + } + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_SALARY, PREFIX_EMPLOYMENT); + + Staff person = createStaffContact(argMultimap); + + return new AddStaffCommand(person); + } + //@@author + + /** + * Creates a {@code Staff} contact based on the argument multimap. + * @param argMultimap Contains the mappings of values to the specific prefixes. + * @return A staff contact. + * @throws ParseException If the user enters invalid parameters. + */ + private Staff createStaffContact(ArgumentMultimap argMultimap) throws ParseException { + try { + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).orElseThrow()); + Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).orElseThrow()); + Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).orElseThrow()); + Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).orElseThrow()); + String noteContent = argMultimap.getValue(PREFIX_NOTE).orElse(DEFAULT_NOTE); + Note note = noteContent.equals(DEFAULT_NOTE) ? new Note(noteContent) : ParserUtil.parseNote(noteContent); + Rating rating = ParserUtil.parseRating(argMultimap.getValue(PREFIX_RATING).orElse(DEFAULT_RATING)); + Tag tag = new Tag(STAFF_TYPE); + Set tags = new HashSet<>(); + tags.add(tag); + Employment employment = ParserUtil.parseEmployment(argMultimap.getValue(PREFIX_EMPLOYMENT).orElseThrow()); + Salary salary = ParserUtil.parseSalary(argMultimap.getValue(PREFIX_SALARY).orElseThrow()); + return new Staff(name, phone, email, address, note, tags, salary, employment, rating); + } catch (ParseException pe) { + throw new ParseException(String.format(AddMessages.MESSAGE_ADD_INVALID_PARAMETERS, pe.getMessage())); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddSupplierCommandParser.java b/src/main/java/seedu/address/logic/parser/AddSupplierCommandParser.java new file mode 100644 index 00000000000..4eb09a61679 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddSupplierCommandParser.java @@ -0,0 +1,105 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.messages.AddMessages.ADD_SUPPLIER; +import static seedu.address.logic.messages.AddMessages.FAILED_TO_ADD; +import static seedu.address.logic.messages.AddMessages.SUPPLIER_TYPE; +import static seedu.address.logic.messages.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.messages.NoteMessages.DEFAULT_NOTE; +import static seedu.address.logic.messages.RateMessages.DEFAULT_RATING; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +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_NOTE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PRICE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PRODUCT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING; + +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.AddSupplierCommand; +import seedu.address.logic.messages.AddMessages; +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.Note; +import seedu.address.model.person.Phone; +import seedu.address.model.person.Price; +import seedu.address.model.person.Product; +import seedu.address.model.person.Rating; +import seedu.address.model.person.Supplier; +import seedu.address.model.tag.Tag; + +//@@author chiageng +/** + * Parses input arguments and creates a new AddSupplierCommand object. + */ +public class AddSupplierCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "argument to pass for add supplier command is null"; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for add supplier command."; + private final Logger logger = LogsCenter.getLogger(getClass()); + /** + * Parses the given {@code String} of arguments in the context of the AddStaffCommand + * and returns an AddCommand object for execution. Parameter {@code args} cannot be null. + * @throws ParseException If the user input does not conform to the expected format. + */ + public AddSupplierCommand parse(String args) throws ParseException { + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_PRODUCT, PREFIX_PRICE, PREFIX_RATING); + + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(args, AddSupplierCommand.MESSAGE_USAGE, ADD_SUPPLIER, + FAILED_TO_ADD, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_PRODUCT, PREFIX_PRICE, PREFIX_RATING); + ParserUtil.verifyNoMissingField(argMultimap, AddSupplierCommand.MESSAGE_USAGE, ADD_SUPPLIER, + FAILED_TO_ADD, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_PRODUCT, PREFIX_PRICE); + boolean isPreambleEmpty = argMultimap.isPreambleEmpty(); + if (!isPreambleEmpty) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddSupplierCommand.MESSAGE_USAGE)); + } + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_PRODUCT, PREFIX_PRICE); + + Supplier person = createSupplierContact(argMultimap); + + return new AddSupplierCommand(person); + } + //@@author + + /** + * Creates a {@code Supplier} contact based on the argument multimap. + * @param argMultimap Contains the mappings of values to the specific prefixes. + * @return A supplier contact. + * @throws ParseException If the user enters invalid parameters. + */ + private Supplier createSupplierContact(ArgumentMultimap argMultimap) throws ParseException { + try { + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).orElseThrow()); + Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).orElseThrow()); + Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).orElseThrow()); + Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).orElseThrow()); + String noteContent = argMultimap.getValue(PREFIX_NOTE).orElse(DEFAULT_NOTE); + Note note = noteContent.equals(DEFAULT_NOTE) ? new Note(noteContent) : ParserUtil.parseNote(noteContent); + Rating rating = ParserUtil.parseRating(argMultimap.getValue(PREFIX_RATING).orElse(DEFAULT_RATING)); + Tag tag = new Tag(SUPPLIER_TYPE); + Set tags = new HashSet<>(); + tags.add(tag); + Price price = ParserUtil.parsePrice(argMultimap.getValue(PREFIX_PRICE).orElseThrow()); + Product product = ParserUtil.parseProduct(argMultimap.getValue(PREFIX_PRODUCT).orElseThrow()); + return new Supplier(name, phone, email, address, note, tags, product, price, rating); + } catch (ParseException pe) { + throw new ParseException(String.format(AddMessages.MESSAGE_ADD_INVALID_PARAMETERS, pe.getMessage())); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index 3149ee07e0b..46a6008c1c2 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -1,7 +1,7 @@ package seedu.address.logic.parser; -import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.address.logic.messages.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.messages.Messages.MESSAGE_UNKNOWN_COMMAND; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -9,20 +9,35 @@ import seedu.address.commons.core.LogsCenter; import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.AddMaintainerCommand; +import seedu.address.logic.commands.AddStaffCommand; +import seedu.address.logic.commands.AddSupplierCommand; import seedu.address.logic.commands.ClearCommand; import seedu.address.logic.commands.Command; import seedu.address.logic.commands.DeleteCommand; import seedu.address.logic.commands.EditCommand; +import seedu.address.logic.commands.EditMaintainerCommand; +import seedu.address.logic.commands.EditStaffCommand; +import seedu.address.logic.commands.EditSupplierCommand; 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.NoteCommand; +import seedu.address.logic.commands.PinCommand; +import seedu.address.logic.commands.RateCommand; +import seedu.address.logic.commands.RedoCommand; +import seedu.address.logic.commands.RemindCommand; +import seedu.address.logic.commands.SearchCommand; +import seedu.address.logic.commands.SortCommand; +import seedu.address.logic.commands.UndoCommand; +import seedu.address.logic.commands.UnpinCommand; import seedu.address.logic.parser.exceptions.ParseException; /** * Parses user input. */ public class AddressBookParser { + public static final String MESSAGE_PARSE_EXCEPTION = "This user input caused a ParseException: "; /** * Used for initial separation of command word and args. @@ -33,9 +48,9 @@ public class AddressBookParser { /** * Parses user input into command for execution. * - * @param userInput full user input string - * @return the command based on the user input - * @throws ParseException if the user input does not conform the expected format + * @param userInput The full user input string. + * @return The command based on the user input. + * @throws ParseException if the user input does not conform to the expected format. */ public Command parseCommand(String userInput) throws ParseException { final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); @@ -44,30 +59,45 @@ public Command parseCommand(String userInput) throws ParseException { } final String commandWord = matcher.group("commandWord"); - final String arguments = matcher.group("arguments"); + final String arguments = ParserUtil.normalisePrefixes(matcher.group("arguments")); // Note to developers: Change the log level in config.json to enable lower level (i.e., FINE, FINER and lower) // log messages such as the one below. // Lower level log messages are used sparingly to minimize noise in the code. logger.fine("Command word: " + commandWord + "; Arguments: " + arguments); - switch (commandWord) { + switch (commandWord.toLowerCase()) { case AddCommand.COMMAND_WORD: return new AddCommandParser().parse(arguments); + case AddStaffCommand.COMMAND_WORD: + return new AddStaffCommandParser().parse(arguments); + + case AddSupplierCommand.COMMAND_WORD: + return new AddSupplierCommandParser().parse(arguments); + + case AddMaintainerCommand.COMMAND_WORD: + return new AddMaintainerCommandParser().parse(arguments); + case EditCommand.COMMAND_WORD: return new EditCommandParser().parse(arguments); + case EditStaffCommand.COMMAND_WORD: + return new EditStaffCommandParser().parse(arguments); + + case EditSupplierCommand.COMMAND_WORD: + return new EditSupplierCommandParser().parse(arguments); + + case EditMaintainerCommand.COMMAND_WORD: + return new EditMaintainerCommandParser().parse(arguments); + case DeleteCommand.COMMAND_WORD: return new DeleteCommandParser().parse(arguments); case ClearCommand.COMMAND_WORD: return new ClearCommand(); - case FindCommand.COMMAND_WORD: - return new FindCommandParser().parse(arguments); - case ListCommand.COMMAND_WORD: return new ListCommand(); @@ -75,12 +105,38 @@ public Command parseCommand(String userInput) throws ParseException { return new ExitCommand(); case HelpCommand.COMMAND_WORD: - return new HelpCommand(); + return new HelpCommandParser().parse(arguments); + + case SearchCommand.COMMAND_WORD: + return new SearchCommandParser().parse(arguments); + + case NoteCommand.COMMAND_WORD: + return new NoteCommandParser().parse(arguments); + + case RateCommand.COMMAND_WORD: + return new RateCommandParser().parse(arguments); + + case UndoCommand.COMMAND_WORD: + return new UndoCommand(); + + case RedoCommand.COMMAND_WORD: + return new RedoCommand(); + + case PinCommand.COMMAND_WORD: + return new PinCommandParser().parse(arguments); + + case UnpinCommand.COMMAND_WORD: + return new UnpinCommandParser().parse(arguments); + + case RemindCommand.COMMAND_WORD: + return new RemindCommand(); + + case SortCommand.COMMAND_WORD: + return new SortCommandParser().parse(arguments); default: - logger.finer("This user input caused a ParseException: " + userInput); + logger.finer(MESSAGE_PARSE_EXCEPTION + userInput); throw new ParseException(MESSAGE_UNKNOWN_COMMAND); } } - } diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java index 21e26887a83..9be3b8bd72a 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java @@ -1,13 +1,16 @@ package seedu.address.logic.parser; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; -import seedu.address.logic.Messages; +import seedu.address.logic.messages.Messages; import seedu.address.logic.parser.exceptions.ParseException; /** @@ -62,6 +65,7 @@ public String getPreamble() { return getValue(new Prefix("")).orElse(""); } + //@@author chiageng /** * Throws a {@code ParseException} if any of the prefixes given in {@code prefixes} appeared more than * once among the arguments. @@ -75,4 +79,97 @@ public void verifyNoDuplicatePrefixesFor(Prefix... prefixes) throws ParseExcepti throw new ParseException(Messages.getErrorMessageForDuplicatePrefixes(duplicatedPrefixes)); } } + //@@author + + //@@author yleeyilin + /** + * Checks that name prefix is not used more than once. + * @return True if there is duplicate name prefix. + */ + public boolean hasDuplicateNamePrefix() { + Prefix[] duplicatedNamePrefixes = Stream.of(PREFIX_NAME).distinct() + .filter(prefix -> argMultimap.containsKey(prefix) && argMultimap.get(prefix).size() > 1) + .toArray(Prefix[]::new); + + boolean isNamePrefixDuplicated = duplicatedNamePrefixes.length > 0; + if (isNamePrefixDuplicated) { + return true; + } + return false; + } + //@@author + + public boolean containsPrefix(Prefix prefix) { + return argMultimap.containsKey(prefix); + } + + //@@author Joshy837 + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ArgumentMultimap)) { + return false; + } + + ArgumentMultimap otherArgumentMultimap = (ArgumentMultimap) other; + return argMultimap.equals(otherArgumentMultimap.argMultimap); + } + //@@author + + public boolean isPreambleEmpty() { + return this.getPreamble().isEmpty(); + } + + //@@author Joshy837 + /** + * Gets all the prefixes. + * @return An array of prefixes in the hashmap. + */ + public Prefix[] getAllPrefixes() { + return argMultimap.keySet().toArray(new Prefix[0]); + } + //@@author + + //@@author Joshy837 + /** + * Throws a {@code ParseException} if any of the prefixes given in {@code prefixes} appeared more than + * once among the arguments. + */ + public void verifyNoEmptyEntries() throws ParseException { + List emptyPrefixes = new ArrayList<>(); + for (Prefix prefix : getAllPrefixes()) { + if (!prefix.toString().isEmpty() && Objects.equals(argMultimap.get(prefix).get(0), "")) { + emptyPrefixes.add(prefix); + } + } + + if (!emptyPrefixes.isEmpty()) { + throw new ParseException(Messages.getErrorMessageForEmptyPrefixes( + emptyPrefixes.toArray(new Prefix[0]))); + } + } + //@@author + + /** + * Returns a string implementation of Argument Multi Map. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("ArgumentMultimap{"); + + for (Map.Entry> entry : argMultimap.entrySet()) { + sb.append(entry.getKey()).append("=").append(entry.getValue()).append(", "); + } + + if (!argMultimap.isEmpty()) { + sb.setLength(sb.length() - 2); + } + sb.append("}"); + return sb.toString(); + } } diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java index 5c9aebfa488..f270c6b6525 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java @@ -3,7 +3,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Tokenizes arguments string of the form: {@code preamble value value ...}
@@ -14,6 +17,8 @@ * in the above example.
*/ public class ArgumentTokenizer { + private static final String PREFIX_VALIDATION_REGEX = ";\\s*([^:]+)\\s*:"; + private static final String ALPHABET_VALIDATION_REGEX = "[A-Za-z]+"; /** * Tokenizes an arguments string and returns an {@code ArgumentMultimap} object that maps prefixes to their @@ -28,6 +33,121 @@ public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) { return extractArguments(argsString, positions); } + //@@author chiageng + /** + * Checks whether exist undetected prefix. + * @param argumentMultimap Argument string that we want to check for undetected prefix + * @param prefixes Prefixes that we accept + * @return array of undetected prefix string + */ + public static ArrayList checkUndetectedPrefix(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + ArrayList undetectedPrefixes = new ArrayList<>(); + + findUndetectedPrefix(undetectedPrefixes, argumentMultimap, prefixes); + return undetectedPrefixes; + } + //@@author + + //@@author chiageng + /** + * Helps to insert undetected prefix into undetectedPrefixes array. + * @param undetectedPrefixes Array to store undetected Prefixes + * @param argumentMultimap Argument string that we want to check for undetected prefix + * @param prefixes Prefixes that we accept + */ + private static void findUndetectedPrefix(ArrayList undetectedPrefixes, ArgumentMultimap argumentMultimap, + Prefix... prefixes) { + for (Prefix p : prefixes) { + if (!arePrefixesPresent(argumentMultimap, p)) { + String field = extractAlphabets(p.getPrefix()); + undetectedPrefixes.add(field); + } + } + } + //@@author + + /** + * 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()); + } + + //@@author chiageng + /** + * Checks whether exist unknown prefix. + * @param argsString Argument string that we want to check for unknown prefix + * @param prefixes Prefixes that we accept + * @return array of unknown prefix string + */ + public static ArrayList checkUnknownPrefix(String argsString, Prefix... prefixes) { + ArrayList unknownPrefixes = new ArrayList<>(); + Pattern pattern = Pattern.compile(PREFIX_VALIDATION_REGEX); + Matcher matcher = pattern.matcher(argsString); + + while (matcher.find()) { + String foundPrefix = matcher.group(0); + findUnknownPrefix(unknownPrefixes, foundPrefix, prefixes); + } + return unknownPrefixes; + } + //@@author + + //@@author chiageng + /** + * Helps to insert unknown prefixes into unknownPrefixes array. + * @param unknownPrefixes Array to store unknown prefixes + * @param foundPrefix Current prefix for checking whether is it unknown prefix or valid prefix + * @param prefixes Prefixes that we accept + */ + private static void findUnknownPrefix(ArrayList unknownPrefixes, String foundPrefix, Prefix... prefixes) { + boolean isKnownPrefix = false; + + for (Prefix p : prefixes) { + String expected = extractAlphabets(p.getPrefix()); + String current = extractAlphabets(foundPrefix); + if (expected.equals(current)) { + isKnownPrefix = true; + break; + } + } + + if (!isKnownPrefix) { + unknownPrefixes.add(extractAlphabets(foundPrefix)); + } + } + //@@author + + //@@author chiageng + /** + * Extracts and concatenates all alphabetical substrings from the provided input string. + * Non-alphabetical characters are ignored, and separate alphabetical substrings are joined + * with a space character in the order they appear in the input. + * + * For example, given the input ";unknown:", the method returns "unknown". + * + * @param input The input string from which to extract alphabetical substrings. + * @return A concatenated string of all alphabetical substrings found in the input, + * separated by spaces if more than one substring is found. If no alphabetical + * characters are present, an empty string is returned. + */ + private static String extractAlphabets(String input) { + StringBuilder result = new StringBuilder(); + Pattern pattern = Pattern.compile(ALPHABET_VALIDATION_REGEX); + Matcher matcher = pattern.matcher(input); + + while (matcher.find()) { + if (result.length() > 0) { + result.append(" "); + } + result.append(matcher.group()); + } + + return result.toString(); + } + //@@author + /** * Finds all zero-based prefix positions in the given arguments string. * @@ -113,8 +233,7 @@ private static ArgumentMultimap extractArguments(String argsString, List { + public static final String MESSAGE_NULL_ARGUMENTS = "argument to pass for delete command is null"; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for delete command."; + public static final String MESSAGE_PARSE_EXCEPTION = "Parsing error while parsing for delete command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); /** * Parses the given {@code String} of arguments in the context of the DeleteCommand - * and returns a DeleteCommand object for execution. - * @throws ParseException if the user input does not conform the expected format + * and returns a DeleteCommand object for execution. Parameter {@code args} cannot be null. + * @throws ParseException If the user input does not conform to the expected format. */ public DeleteCommand parse(String args) throws ParseException { + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME); + + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(args, DeleteCommand.MESSAGE_USAGE, DELETE, + FAILED_TO_DELETE, PREFIX_NAME); + ParserUtil.verifyNoMissingField(argMultimap, DeleteCommand.MESSAGE_USAGE, DELETE, + FAILED_TO_DELETE, PREFIX_NAME); + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS); + boolean isPreambleEmpty = argMultimap.getPreamble().isEmpty(); + if (!isPreambleEmpty) { + logger.log(Level.WARNING, MESSAGE_PARSE_EXCEPTION); + throw new ParseException(String.format(DeleteMessages.MESSAGE_DELETE_MISSING_NAME, + DeleteCommand.MESSAGE_USAGE)); + } + try { - Index index = ParserUtil.parseIndex(args); - return new DeleteCommand(index); + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).orElseThrow()); + return new DeleteCommand(name); } catch (ParseException pe) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe); + throw new ParseException(String.format(DeleteMessages.MESSAGE_DELETE_INVALID_PARAMETERS, pe.getMessage())); } } - } diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java index 46b3309a78b..cf94ddad1ea 100644 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java @@ -1,85 +1,115 @@ package seedu.address.logic.parser; import static java.util.Objects.requireNonNull; -import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.messages.AddMessages.OTHER_TYPE; +import static seedu.address.logic.messages.EditMessages.EDIT; +import static seedu.address.logic.messages.EditMessages.FAILED_TO_EDIT; +import static seedu.address.logic.messages.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_FIELD; 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_TAG; -import java.util.Collection; -import java.util.Collections; -import java.util.Optional; +import java.util.HashSet; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; -import seedu.address.commons.core.index.Index; +import seedu.address.commons.core.LogsCenter; import seedu.address.logic.commands.EditCommand; import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.address.logic.messages.EditMessages; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Name; import seedu.address.model.tag.Tag; +//@@author yleeyilin /** - * Parses input arguments and creates a new EditCommand object + * Parses input arguments and creates a new EditCommand object. */ public class EditCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "Arguments to pass into edit command is null."; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for edit command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); /** - * Parses the given {@code String} of arguments in the context of the EditCommand - * and returns an EditCommand object for execution. - * @throws ParseException if the user input does not conform the expected format + * Parses the given {@code String} of arguments in the context of the EditCommand. + * and returns an EditCommand object for execution. Parameter {@code args} cannot be null. + * @throws ParseException If the user input does not conform to the expected format. */ public EditCommand parse(String args) throws ParseException { requireNonNull(args); - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + assert (args != null) : MESSAGE_NULL_ARGUMENTS; - Index index; + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); - try { - index = ParserUtil.parseIndex(argMultimap.getPreamble()); - } catch (ParseException pe) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); + String parsedArgs = ParserUtil.parseArg(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(parsedArgs, PREFIX_NAME, PREFIX_FIELD); + + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(parsedArgs, EditCommand.MESSAGE_USAGE, EDIT, + FAILED_TO_EDIT, + PREFIX_NAME, PREFIX_FIELD, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS); + ParserUtil.verifyNoMissingField(argMultimap, EditCommand.MESSAGE_USAGE, EDIT, + FAILED_TO_EDIT, + PREFIX_NAME, PREFIX_FIELD); + boolean isNamePrefixDuplicated = argMultimap.hasDuplicateNamePrefix(); + if (isNamePrefixDuplicated) { + throw new ParseException(String.format(EditMessages.MESSAGE_MULTIPLE_NAME, + EditCommand.MESSAGE_USAGE)); } - argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS); + // maps user commands to name and field + Name name = ParserUtil.mapName(argMultimap, EditMessages.MESSAGE_EDIT_INVALID_NAME); + String fieldArgs = ParserUtil.mapFields(argMultimap, String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditCommand.MESSAGE_USAGE)); - EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); + // maps fields to edit to their values + ArgumentMultimap fieldArgMultimap = + ArgumentTokenizer.tokenize(fieldArgs, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS); - if (argMultimap.getValue(PREFIX_NAME).isPresent()) { - editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); - } - if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { - editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); - } - if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { - editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); - } - if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { - editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); - } - parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); + fieldArgMultimap.verifyNoDuplicatePrefixesFor(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS); + + EditPersonDescriptor editPersonDescriptor = editPersonDescription(fieldArgMultimap); - if (!editPersonDescriptor.isAnyFieldEdited()) { - throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); + boolean isNoFieldEdited = !editPersonDescriptor.isAnyFieldEdited(); + if (isNoFieldEdited) { + throw new ParseException(EditMessages.MESSAGE_EDIT_EMPTY_FIELD); } - return new EditCommand(index, editPersonDescriptor); + Set tags = new HashSet<>(); + tags.add(new Tag(OTHER_TYPE)); + editPersonDescriptor.setTags(tags); + return new EditCommand(name, editPersonDescriptor); } /** - * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. - * If {@code tags} contain only one element which is an empty string, it will be parsed into a - * {@code Set} containing zero tags. + * Edits the description of a {@code Person}. + * + * @param fieldArgMultimap The mapping of field arguments into different specific fields. + * @return EditPersonDescriptor that contains the new values from the user. + * @throws ParseException If the user enters invalid parameters. */ - private Optional> parseTagsForEdit(Collection tags) throws ParseException { - assert tags != null; + private EditPersonDescriptor editPersonDescription(ArgumentMultimap fieldArgMultimap) throws ParseException { + try { + EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); - if (tags.isEmpty()) { - return Optional.empty(); + if (fieldArgMultimap.getValue(PREFIX_PHONE).isPresent()) { + editPersonDescriptor.setPhone(ParserUtil.parsePhone(fieldArgMultimap.getValue(PREFIX_PHONE).get())); + } + if (fieldArgMultimap.getValue(PREFIX_EMAIL).isPresent()) { + editPersonDescriptor.setEmail(ParserUtil.parseEmail(fieldArgMultimap.getValue(PREFIX_EMAIL).get())); + } + if (fieldArgMultimap.getValue(PREFIX_ADDRESS).isPresent()) { + editPersonDescriptor.setAddress(ParserUtil.parseAddress( + fieldArgMultimap.getValue(PREFIX_ADDRESS).get())); + } + + return editPersonDescriptor; + } catch (ParseException pe) { + throw new ParseException(String.format(EditMessages.MESSAGE_EDIT_INVALID_FIELD, pe.getMessage())); } - Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; - return Optional.of(ParserUtil.parseTags(tagSet)); } - } diff --git a/src/main/java/seedu/address/logic/parser/EditMaintainerCommandParser.java b/src/main/java/seedu/address/logic/parser/EditMaintainerCommandParser.java new file mode 100644 index 00000000000..6aeed3586e6 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/EditMaintainerCommandParser.java @@ -0,0 +1,129 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.messages.AddMessages.MAINTAINER_TYPE; +import static seedu.address.logic.messages.EditMessages.EDIT_MAINTAINER; +import static seedu.address.logic.messages.EditMessages.FAILED_TO_EDIT; +import static seedu.address.logic.messages.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMMISSION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_FIELD; +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_SKILL; + +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.EditMaintainerCommand; +import seedu.address.logic.commands.EditMaintainerCommand.EditMaintainerDescriptor; +import seedu.address.logic.messages.EditMessages; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Name; +import seedu.address.model.tag.Tag; + +//@@author yleeyilin +/** + * Parses input arguments and creates a new EditMaintainerCommand object. + */ +public class EditMaintainerCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "Arguments to pass into edit maintainer command is null."; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for edit maintainer command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Parses the given {@code String} of arguments in the context of the EditMaintainerCommand. + * and returns an EditMaintainerCommand object for execution. Parameter {@code args} cannot be null. + * @throws ParseException If the user input does not conform to the expected format + */ + public EditMaintainerCommand parse(String args) throws ParseException { + requireNonNull(args); + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + + String parsedArgs = ParserUtil.parseArg(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(parsedArgs, PREFIX_NAME, PREFIX_FIELD); + + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(parsedArgs, EditMaintainerCommand.MESSAGE_USAGE, EDIT_MAINTAINER, + FAILED_TO_EDIT, + PREFIX_NAME, PREFIX_FIELD, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_SKILL, PREFIX_COMMISSION); + ParserUtil.verifyNoMissingField(argMultimap, EditMaintainerCommand.MESSAGE_USAGE, EDIT_MAINTAINER, + FAILED_TO_EDIT, + PREFIX_NAME, PREFIX_FIELD); + boolean isNamePrefixDuplicated = argMultimap.hasDuplicateNamePrefix(); + if (isNamePrefixDuplicated) { + throw new ParseException(String.format(EditMessages.MESSAGE_MULTIPLE_NAME, + EditMaintainerCommand.MESSAGE_USAGE)); + } + + // maps user commands to name and field + Name name = ParserUtil.mapName(argMultimap, EditMessages.MESSAGE_EDIT_INVALID_NAME); + String fieldArgs = ParserUtil.mapFields(argMultimap, String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditMaintainerCommand.MESSAGE_USAGE)); + + // maps fields to edit to their values + ArgumentMultimap fieldArgMultimap = + ArgumentTokenizer.tokenize(fieldArgs, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_SKILL, PREFIX_COMMISSION); + + fieldArgMultimap.verifyNoDuplicatePrefixesFor(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_SKILL, PREFIX_COMMISSION); + + EditMaintainerDescriptor editMaintainerDescriptor = editMaintainerDescription(fieldArgMultimap); + + boolean isNoFieldEdited = !editMaintainerDescriptor.isAnyFieldEdited(); + if (isNoFieldEdited) { + throw new ParseException(EditMessages.MESSAGE_EDIT_EMPTY_FIELD); + } + + Set tags = new HashSet<>(); + tags.add(new Tag(MAINTAINER_TYPE)); + editMaintainerDescriptor.setTags(tags); + + return new EditMaintainerCommand(name, editMaintainerDescriptor); + } + + /** + * Edits the description of a {@code Maintainer}. + * + * @param fieldArgMultimap The mapping of field arguments into different specific fields. + * @return EditMaintainerDescriptor that contains the new values from the user. + * @throws ParseException If the user enters invalid parameters. + */ + private EditMaintainerDescriptor editMaintainerDescription( + ArgumentMultimap fieldArgMultimap) throws ParseException { + try { + EditMaintainerDescriptor editMaintainerDescriptor = new EditMaintainerDescriptor(); + + if (fieldArgMultimap.getValue(PREFIX_PHONE).isPresent()) { + editMaintainerDescriptor.setPhone(ParserUtil.parsePhone(fieldArgMultimap.getValue(PREFIX_PHONE).get())); + } + if (fieldArgMultimap.getValue(PREFIX_EMAIL).isPresent()) { + editMaintainerDescriptor.setEmail(ParserUtil.parseEmail(fieldArgMultimap.getValue(PREFIX_EMAIL).get())); + } + if (fieldArgMultimap.getValue(PREFIX_ADDRESS).isPresent()) { + editMaintainerDescriptor.setAddress(ParserUtil.parseAddress( + fieldArgMultimap.getValue(PREFIX_ADDRESS).get())); + } + if (fieldArgMultimap.getValue(PREFIX_SKILL).isPresent()) { + editMaintainerDescriptor.setSkill(ParserUtil.parseSkill(fieldArgMultimap.getValue(PREFIX_SKILL).get())); + } + if (fieldArgMultimap.getValue(PREFIX_COMMISSION).isPresent()) { + editMaintainerDescriptor.setCommission(ParserUtil.parseCommission( + fieldArgMultimap.getValue(PREFIX_COMMISSION).get())); + } + + return editMaintainerDescriptor; + } catch (ParseException pe) { + throw new ParseException(String.format(EditMessages.MESSAGE_EDIT_INVALID_FIELD, pe.getMessage())); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/EditStaffCommandParser.java b/src/main/java/seedu/address/logic/parser/EditStaffCommandParser.java new file mode 100644 index 00000000000..9ce0f803712 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/EditStaffCommandParser.java @@ -0,0 +1,127 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.messages.AddMessages.STAFF_TYPE; +import static seedu.address.logic.messages.EditMessages.EDIT_STAFF; +import static seedu.address.logic.messages.EditMessages.FAILED_TO_EDIT; +import static seedu.address.logic.messages.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMPLOYMENT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_FIELD; +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_SALARY; + +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.EditStaffCommand; +import seedu.address.logic.commands.EditStaffCommand.EditStaffDescriptor; +import seedu.address.logic.messages.EditMessages; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Name; +import seedu.address.model.tag.Tag; + +//@@author yleeyilin +/** + * Parses input arguments and creates a new EditStaffCommand object. + */ +public class EditStaffCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "Arguments to pass for edit staff command is null."; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for edit staff command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Parses the given {@code String} of arguments in the context of the EditStaffCommand. + * and returns an EditStaffCommand object for execution. Parameter {@code args} cannot be null. + * @throws ParseException If the user input does not conform to the expected format + */ + public EditStaffCommand parse(String args) throws ParseException { + requireNonNull(args); + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + + String parsedArgs = ParserUtil.parseArg(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(parsedArgs, PREFIX_NAME, PREFIX_FIELD); + + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(args, EditStaffCommand.MESSAGE_USAGE, EDIT_STAFF, + FAILED_TO_EDIT, PREFIX_NAME, + PREFIX_FIELD, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_EMPLOYMENT, PREFIX_SALARY); + ParserUtil.verifyNoMissingField(argMultimap, EditStaffCommand.MESSAGE_USAGE, EDIT_STAFF, + FAILED_TO_EDIT, + PREFIX_NAME, PREFIX_FIELD); + boolean isNamePrefixDuplicated = argMultimap.hasDuplicateNamePrefix(); + if (isNamePrefixDuplicated) { + throw new ParseException(String.format(EditMessages.MESSAGE_MULTIPLE_NAME, + EditStaffCommand.MESSAGE_USAGE)); + } + + // maps user commands to name and field + Name name = ParserUtil.mapName(argMultimap, EditMessages.MESSAGE_EDIT_INVALID_NAME); + String fieldArgs = ParserUtil.mapFields(argMultimap, String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditStaffCommand.MESSAGE_USAGE)); + + // maps fields to edit to their values + ArgumentMultimap fieldArgMultimap = + ArgumentTokenizer.tokenize(fieldArgs, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_EMPLOYMENT, PREFIX_SALARY); + + fieldArgMultimap.verifyNoDuplicatePrefixesFor(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_EMPLOYMENT, PREFIX_SALARY); + + EditStaffDescriptor editStaffDescriptor = editStaffDescription(fieldArgMultimap); + + boolean isNoFieldEdited = !editStaffDescriptor.isAnyFieldEdited(); + if (isNoFieldEdited) { + throw new ParseException(EditMessages.MESSAGE_EDIT_EMPTY_FIELD); + } + + Set tags = new HashSet<>(); + tags.add(new Tag(STAFF_TYPE)); + editStaffDescriptor.setTags(tags); + + return new EditStaffCommand(name, editStaffDescriptor); + } + + /** + * Edits the description of a {@code Staff}. + * + * @param fieldArgMultimap The mapping of field arguments into different specific fields. + * @return EditStaffDescriptor that contains the new values from the user. + * @throws ParseException If the user enters invalid parameters. + */ + private EditStaffDescriptor editStaffDescription(ArgumentMultimap fieldArgMultimap) throws ParseException { + try { + EditStaffDescriptor editStaffDescriptor = new EditStaffDescriptor(); + + if (fieldArgMultimap.getValue(PREFIX_PHONE).isPresent()) { + editStaffDescriptor.setPhone(ParserUtil.parsePhone(fieldArgMultimap.getValue(PREFIX_PHONE).get())); + } + if (fieldArgMultimap.getValue(PREFIX_EMAIL).isPresent()) { + editStaffDescriptor.setEmail(ParserUtil.parseEmail(fieldArgMultimap.getValue(PREFIX_EMAIL).get())); + } + if (fieldArgMultimap.getValue(PREFIX_ADDRESS).isPresent()) { + editStaffDescriptor.setAddress(ParserUtil.parseAddress( + fieldArgMultimap.getValue(PREFIX_ADDRESS).get())); + } + if (fieldArgMultimap.getValue(PREFIX_SALARY).isPresent()) { + editStaffDescriptor.setSalary(ParserUtil.parseSalary(fieldArgMultimap.getValue(PREFIX_SALARY).get())); + } + if (fieldArgMultimap.getValue(PREFIX_EMPLOYMENT).isPresent()) { + editStaffDescriptor.setEmployment(ParserUtil.parseEmployment( + fieldArgMultimap.getValue(PREFIX_EMPLOYMENT).get())); + } + + return editStaffDescriptor; + } catch (ParseException pe) { + throw new ParseException(String.format(EditMessages.MESSAGE_EDIT_INVALID_FIELD, pe.getMessage())); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/EditSupplierCommandParser.java b/src/main/java/seedu/address/logic/parser/EditSupplierCommandParser.java new file mode 100644 index 00000000000..85d6c05e202 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/EditSupplierCommandParser.java @@ -0,0 +1,127 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.messages.AddMessages.SUPPLIER_TYPE; +import static seedu.address.logic.messages.EditMessages.EDIT_SUPPLIER; +import static seedu.address.logic.messages.EditMessages.FAILED_TO_EDIT; +import static seedu.address.logic.messages.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_FIELD; +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_PRICE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PRODUCT; + +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.EditSupplierCommand; +import seedu.address.logic.commands.EditSupplierCommand.EditSupplierDescriptor; +import seedu.address.logic.messages.EditMessages; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Name; +import seedu.address.model.tag.Tag; + +//@@author yleeyilin +/** + * Parses input arguments and creates a new EditSupplierCommand object. + */ +public class EditSupplierCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "Arguments to pass into edit supplier command is null."; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for edit supplier command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Parses the given {@code String} of arguments in the context of the EditSupplierCommand + * and returns an EditSupplierCommand object for execution. Parameter {@code args} cannot be null. + * @throws ParseException If the user input does not conform to the expected format + */ + public EditSupplierCommand parse(String args) throws ParseException { + requireNonNull(args); + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + + String parsedArgs = ParserUtil.parseArg(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(parsedArgs, PREFIX_NAME, PREFIX_FIELD); + + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(parsedArgs, EditSupplierCommand.MESSAGE_USAGE, EDIT_SUPPLIER, + FAILED_TO_EDIT, + PREFIX_NAME, PREFIX_FIELD, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_NAME, PREFIX_PRODUCT, PREFIX_PRICE); + ParserUtil.verifyNoMissingField(argMultimap, EditSupplierCommand.MESSAGE_USAGE, EDIT_SUPPLIER, + FAILED_TO_EDIT, + PREFIX_NAME, PREFIX_FIELD); + boolean isNamePrefixDuplicated = argMultimap.hasDuplicateNamePrefix(); + if (isNamePrefixDuplicated) { + throw new ParseException(String.format(EditMessages.MESSAGE_MULTIPLE_NAME, + EditSupplierCommand.MESSAGE_USAGE)); + } + + // maps user commands to name and field + Name name = ParserUtil.mapName(argMultimap, EditMessages.MESSAGE_EDIT_INVALID_NAME); + String fieldArgs = ParserUtil.mapFields(argMultimap, String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditSupplierCommand.MESSAGE_USAGE)); + + // maps fields to edit to their values + ArgumentMultimap fieldArgMultimap = + ArgumentTokenizer.tokenize(fieldArgs, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_NAME, PREFIX_PRODUCT, PREFIX_PRICE); + + fieldArgMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS); + + EditSupplierDescriptor editSupplierDescriptor = editSupplierDescription(fieldArgMultimap); + + boolean isNoFieldEdited = !editSupplierDescriptor.isAnyFieldEdited(); + if (isNoFieldEdited) { + throw new ParseException(EditMessages.MESSAGE_EDIT_EMPTY_FIELD); + } + + Set tags = new HashSet<>(); + tags.add(new Tag(SUPPLIER_TYPE)); + editSupplierDescriptor.setTags(tags); + + return new EditSupplierCommand(name, editSupplierDescriptor); + } + + /** + * Edits the description of a {@code Supplier}. + * + * @param fieldArgMultimap The mapping of field arguments into different specific fields. + * @return EditSupplierDescriptor that contains the new values from the user. + * @throws ParseException If the user enters invalid parameters. + */ + private EditSupplierDescriptor editSupplierDescription(ArgumentMultimap fieldArgMultimap) throws ParseException { + try { + EditSupplierDescriptor editSupplierDescriptor = new EditSupplierDescriptor(); + + if (fieldArgMultimap.getValue(PREFIX_PHONE).isPresent()) { + editSupplierDescriptor.setPhone(ParserUtil.parsePhone(fieldArgMultimap.getValue(PREFIX_PHONE).get())); + } + if (fieldArgMultimap.getValue(PREFIX_EMAIL).isPresent()) { + editSupplierDescriptor.setEmail(ParserUtil.parseEmail(fieldArgMultimap.getValue(PREFIX_EMAIL).get())); + } + if (fieldArgMultimap.getValue(PREFIX_ADDRESS).isPresent()) { + editSupplierDescriptor.setAddress(ParserUtil.parseAddress( + fieldArgMultimap.getValue(PREFIX_ADDRESS).get())); + } + if (fieldArgMultimap.getValue(PREFIX_PRODUCT).isPresent()) { + editSupplierDescriptor.setProduct(ParserUtil.parseProduct( + fieldArgMultimap.getValue(PREFIX_PRODUCT).get())); + } + if (fieldArgMultimap.getValue(PREFIX_PRICE).isPresent()) { + editSupplierDescriptor.setPrice(ParserUtil.parsePrice(fieldArgMultimap.getValue(PREFIX_PRICE).get())); + } + + return editSupplierDescriptor; + } catch (ParseException pe) { + throw new ParseException(String.format(EditMessages.MESSAGE_EDIT_INVALID_FIELD, pe.getMessage())); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java deleted file mode 100644 index 2867bde857b..00000000000 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ /dev/null @@ -1,33 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; - -import java.util.Arrays; - -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -/** - * Parses input arguments and creates a new FindCommand object - */ -public class FindCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the FindCommand - * and returns a FindCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public FindCommand parse(String args) throws ParseException { - String trimmedArgs = args.trim(); - if (trimmedArgs.isEmpty()) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); - } - - String[] nameKeywords = trimmedArgs.split("\\s+"); - - return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); - } - -} 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..b39a179485e --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/HelpCommandParser.java @@ -0,0 +1,59 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.messages.HelpMessages.FAILED_TO_HELP; +import static seedu.address.logic.messages.HelpMessages.HELP; +import static seedu.address.logic.parser.CliSyntax.PREFIX_HELP; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.HelpCommand; +import seedu.address.logic.messages.HelpMessages; +import seedu.address.logic.parser.exceptions.ParseException; + +//@@author jannaleong +/** + * Parses input arguments and creates a new HelpCommand object. + */ +public class HelpCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "Argument to pass into help command is null."; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for help command."; + public static final String MESSAGE_PARSE_EXCEPTION = "Parsing error while parsing for help command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Parses the given {@code String} of arguments in the context of the HelpCommand and + * returns a HelpCommand object for execution. Parameter {@code args} cannot be null. + * + * @param args Argument to parse. + * @return HelpCommand object with the parsed command type value. + * @throws ParseException If the user input does not conform to the expected format. + */ + public HelpCommand parse(String args) throws ParseException { + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_HELP); + + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(args, HelpCommand.MESSAGE_USAGE, HELP, + FAILED_TO_HELP, PREFIX_HELP); + ParserUtil.verifyNoMissingField(argMultimap, HelpCommand.MESSAGE_USAGE, HELP, + FAILED_TO_HELP, PREFIX_HELP); + boolean isPreambleEmpty = argMultimap.isPreambleEmpty(); + if (!isPreambleEmpty) { + logger.log(Level.WARNING, MESSAGE_PARSE_EXCEPTION); + throw new ParseException(String.format(HelpMessages.MESSAGE_HELP_MISSING_COMMAND, + HelpCommand.MESSAGE_USAGE)); + } + + try { + String commandType = ParserUtil.parseHelp(argMultimap.getValue(PREFIX_HELP).orElseThrow()); + return new HelpCommand(commandType); + } catch (ParseException pe) { + throw new ParseException(String.format(HelpMessages.MESSAGE_HELP_INVALID_PARAMETERS, pe.getMessage())); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/NoteCommandParser.java b/src/main/java/seedu/address/logic/parser/NoteCommandParser.java new file mode 100644 index 00000000000..7ba87db99d6 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/NoteCommandParser.java @@ -0,0 +1,71 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.messages.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.messages.NoteMessages.FAILED_TO_ADD_NOTE; +import static seedu.address.logic.messages.NoteMessages.NOTE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.NoteCommand; +import seedu.address.logic.messages.NoteMessages; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Name; +import seedu.address.model.person.Note; + +//@@author jannaleong +/** + * Parses input arguments and creates a new NoteCommand object. + */ +public class NoteCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "Argument to pass into note command is null."; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for note command."; + public static final String MESSAGE_PARSE_EXCEPTION = "Parsing error while parsing for note command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Parses the given {@code String} of arguments in the context of the NoteCommand + * and returns a NoteCommand object for execution. Parameter {@code args} cannot be null. + * + * @param args Argument to parse. + * @return NoteCommand object with the parsed values. + * @throws ParseException If the user input does not conform to the expected format. + */ + public NoteCommand parse(String args) throws ParseException { + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_NOTE, PREFIX_DEADLINE); + + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(args, NoteCommand.MESSAGE_USAGE, NOTE, + FAILED_TO_ADD_NOTE, PREFIX_NAME, PREFIX_NOTE, PREFIX_DEADLINE); + ParserUtil.verifyNoMissingField(argMultimap, NoteCommand.MESSAGE_USAGE, NOTE, + FAILED_TO_ADD_NOTE, PREFIX_NAME, PREFIX_NOTE); + boolean isPreambleEmpty = argMultimap.isPreambleEmpty(); + if (!isPreambleEmpty) { + logger.log(Level.WARNING, MESSAGE_PARSE_EXCEPTION); + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, NoteCommand.MESSAGE_USAGE)); + } + + try { + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + boolean isContainingDeadlinePrefix = argMultimap.containsPrefix(PREFIX_DEADLINE); + Note note; + if (!isContainingDeadlinePrefix) { + note = ParserUtil.parseNote(argMultimap.getValue(PREFIX_NOTE).get()); + } else { + note = ParserUtil.parseDeadlineNote(argMultimap.getValue(PREFIX_NOTE).get(), + argMultimap.getValue(PREFIX_DEADLINE).get()); + } + return new NoteCommand(name, note); + } catch (ParseException pe) { + throw new ParseException(String.format(NoteMessages.MESSAGE_NOTE_INVALID_PARAMETERS, pe.getMessage())); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/address/logic/parser/Parser.java index d6551ad8e3f..da8ef8c811b 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/seedu/address/logic/parser/Parser.java @@ -10,7 +10,7 @@ public interface Parser { /** * Parses {@code userInput} into a command and returns it. - * @throws ParseException if {@code userInput} does not conform the expected format + * @throws ParseException If {@code userInput} does not conform the expected format. */ T parse(String userInput) throws ParseException; } diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index b117acb9c55..9ea9c4f7ea2 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -1,31 +1,53 @@ package seedu.address.logic.parser; import static java.util.Objects.requireNonNull; +import static seedu.address.logic.messages.Messages.MESSAGE_COMMAND_FORMAT; +import static seedu.address.logic.messages.Messages.MESSAGE_MISSING_FIELD_FORMAT; +import static seedu.address.logic.messages.Messages.MESSAGE_UNKNOWN_FIELD_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_FIELD; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SORT_COLLECTION; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Stream; +import seedu.address.commons.core.LogsCenter; import seedu.address.commons.core.index.Index; import seedu.address.commons.util.StringUtil; +import seedu.address.logic.commands.HelpCommand; +import seedu.address.logic.messages.NoteMessages; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.person.Address; +import seedu.address.model.person.Commission; +import seedu.address.model.person.DeadlineNote; import seedu.address.model.person.Email; +import seedu.address.model.person.Employment; import seedu.address.model.person.Name; +import seedu.address.model.person.Note; import seedu.address.model.person.Phone; +import seedu.address.model.person.Price; +import seedu.address.model.person.Product; +import seedu.address.model.person.Rating; +import seedu.address.model.person.Salary; +import seedu.address.model.person.Skill; import seedu.address.model.tag.Tag; /** * Contains utility methods used for parsing strings in the various *Parser classes. */ public class ParserUtil { - public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; + private static final Logger logger = LogsCenter.getLogger("ParselUtil"); /** * Parses {@code oneBasedIndex} into an {@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). + * @throws ParseException If the specified index is invalid (not non-zero unsigned integer). */ public static Index parseIndex(String oneBasedIndex) throws ParseException { String trimmedIndex = oneBasedIndex.trim(); @@ -38,23 +60,33 @@ public static Index parseIndex(String oneBasedIndex) throws ParseException { /** * Parses a {@code String name} into a {@code Name}. * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code name} is invalid. + * @throws ParseException If the given {@code name} is invalid. */ public static Name parseName(String name) throws ParseException { requireNonNull(name); String trimmedName = name.trim(); + if (!Name.isValidName(trimmedName)) { throw new ParseException(Name.MESSAGE_CONSTRAINTS); } return new Name(trimmedName); } + /** + * Parses a {@code String arg} into a {@code arg}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException If the given {@code name} is invalid. + */ + public static String parseArg(String arg) throws ParseException { + requireNonNull(arg); + String parserArg = arg.replaceAll("field : \\{ ", "field : { ; "); + return parserArg; + } + /** * Parses a {@code String phone} into a {@code Phone}. * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code phone} is invalid. + * @throws ParseException If the given {@code phone} is invalid. */ public static Phone parsePhone(String phone) throws ParseException { requireNonNull(phone); @@ -68,8 +100,7 @@ public static Phone parsePhone(String phone) throws ParseException { /** * Parses a {@code String address} into an {@code Address}. * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code address} is invalid. + * @throws ParseException If the given {@code address} is invalid. */ public static Address parseAddress(String address) throws ParseException { requireNonNull(address); @@ -83,8 +114,7 @@ public static Address parseAddress(String address) throws ParseException { /** * Parses a {@code String email} into an {@code Email}. * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code email} is invalid. + * @throws ParseException If the given {@code email} is invalid. */ public static Email parseEmail(String email) throws ParseException { requireNonNull(email); @@ -95,11 +125,107 @@ public static Email parseEmail(String email) throws ParseException { return new Email(trimmedEmail); } + //@@author chiageng /** - * Parses a {@code String tag} into a {@code Tag}. + * Parses a {@code String employment} into an {@code Employment}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException If the given {@code employment} is invalid. + */ + public static Employment parseEmployment(String employment) throws ParseException { + requireNonNull(employment); + String trimmedEmployment = employment.trim(); + if (!Employment.isValidEmployment(trimmedEmployment)) { + throw new ParseException(Employment.MESSAGE_CONSTRAINTS); + } + return new Employment(trimmedEmployment); + } + //@@author + + //@@author chiageng + /** + * Parses a {@code String salary} into an {@code Salary}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException If the given {@code salary} is invalid. + */ + public static Salary parseSalary(String salary) throws ParseException { + requireNonNull(salary); + String trimmedSalary = salary.trim(); + if (!Salary.isValidSalary(trimmedSalary)) { + throw new ParseException(Salary.MESSAGE_CONSTRAINTS); + } + return new Salary(trimmedSalary); + } + //@@author + + //@@author chiageng + /** + * Parses a {@code String product} into an {@code Product}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException If the given {@code product} is invalid. + */ + public static Product parseProduct(String product) throws ParseException { + requireNonNull(product); + String trimmedProduct = product.trim(); + if (!Product.isValidProduct(trimmedProduct)) { + throw new ParseException(Product.MESSAGE_CONSTRAINTS); + } + return new Product(trimmedProduct); + } + //@@author + + //@@author chiageng + /** + * Parses a {@code String price} into an {@code Price}. * Leading and trailing whitespaces will be trimmed. * - * @throws ParseException if the given {@code tag} is invalid. + * @throws ParseException If the given {@code price} is invalid. + */ + public static Price parsePrice(String price) throws ParseException { + requireNonNull(price); + String trimmedPrice = price.trim(); + if (!Price.isValidPrice(trimmedPrice)) { + throw new ParseException(Price.MESSAGE_CONSTRAINTS); + } + return new Price(trimmedPrice); + } + //@@author + + //@@author chiageng + /** + * Parses a {@code String skill} into an {@code Skill}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException If the given {@code skill} is invalid. + */ + public static Skill parseSkill(String skill) throws ParseException { + requireNonNull(skill); + String trimmedSkill = skill.trim(); + if (!Skill.isValidSkill(trimmedSkill)) { + throw new ParseException(Skill.MESSAGE_CONSTRAINTS); + } + return new Skill(trimmedSkill); + } + //@@author + + //@@author chiageng + /** + * Parses a {@code String commission} into an {@code Commission}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException If the given {@code commission} is invalid. + */ + public static Commission parseCommission(String commission) throws ParseException { + requireNonNull(commission); + String trimmedCommission = commission.trim(); + if (!Commission.isValidCommission(trimmedCommission)) { + throw new ParseException(Commission.MESSAGE_CONSTRAINTS); + } + return new Commission(trimmedCommission); + } + //@@author + + /** + * Parses a {@code String tag} into a {@code Tag}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException If the given {@code tag} is invalid. */ public static Tag parseTag(String tag) throws ParseException { requireNonNull(tag); @@ -121,4 +247,252 @@ public static Set parseTags(Collection tags) throws ParseException } return tagSet; } + + //@@author yleeyilin + /** + * Parses a {@code String args} into an {@code String args}. + * Leading and trailing whitespaces and curly brackets will be trimmed. + */ + public static String parseField(String args) throws ParseException { + requireNonNull(args); + String trimmedFields = args.replaceAll("[{}]", "").trim(); + return trimmedFields; + } + //@@author + + //@@author jannaleong + /** + * Parses a {@code String note} into a {@code Note}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException If the given {@code note} is invalid. + */ + public static Note parseNote(String note) throws ParseException { + requireNonNull(note); + String trimmedNote = note.trim(); + + if (Note.isNoteContainingDeadline(trimmedNote)) { + throw new ParseException(NoteMessages.MESSAGE_DEADLINE_NOT_SPECIFIED); + } + if (!Note.isValidNote(trimmedNote)) { + throw new ParseException(Note.MESSAGE_CONSTRAINTS); + } + return new Note(trimmedNote); + } + //@@author + + //@@author jamessinmaojun + /** + * Parses a {@code String rating} into a {@code Rating}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException If the given {@code rating} is invalid. + */ + public static Rating parseRating(String rating) throws ParseException { + requireNonNull(rating); + String trimmedRating = rating.trim(); + if (!Rating.isValidRating(trimmedRating)) { + throw new ParseException(Rating.MESSAGE_CONSTRAINTS); + } + return new Rating(trimmedRating); + } + //@@author + + //@@author jannaleong + /** + * Parses a {@code String note, String deadline} into a {@code DeadlineNote}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException If the given {@code note, deadline} is invalid. + */ + public static DeadlineNote parseDeadlineNote(String note, String deadline) throws ParseException { + requireNonNull(note, deadline); + String trimmedNote = note.trim(); + String trimmedDeadline = deadline.trim(); + + if (!DeadlineNote.isValidNote(trimmedNote)) { + throw new ParseException(Note.MESSAGE_CONSTRAINTS); + } + if (!DeadlineNote.isValidDate(deadline)) { + throw new ParseException(DeadlineNote.MESSAGE_INVALID_DATE); + } + return new DeadlineNote(trimmedNote, trimmedDeadline); + } + //@@author + + //@@author jannaleong + /** + * Parses a {@code String commandType}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException If the given {@code commandType} is invalid. + */ + public static String parseHelp(String commandType) throws ParseException { + requireNonNull(commandType); + String trimmedCommand = commandType.trim(); + + if (!HelpCommand.isValidCommandType(commandType)) { + throw new ParseException(HelpCommand.MESSAGE_CONSTRAINTS); + } + return trimmedCommand; + } + //@@author + + //@@author Joshy837 + /** + * Parses a {@code String sort field}. + * Leading "; " and trailing " : " will be trimmed + * @throws ParseException If the given {@code commandType} is invalid. + */ + public static String parseSortField(String sortField) throws ParseException { + + requireNonNull(sortField); + + // Remove "; " using replace() + String removedSemicolon = sortField.replace("; ", ""); + + // Remove " : " using replaceAll() + String trimmedSortField = removedSemicolon.replaceAll(" : ", ""); + + return trimmedSortField; + } + //@@author + + //@@author yleeyilin + /** + * Standardises all prefixes input by users to lower case. + * @param argsString The input string to parse. + * @return The formatted input string to fix. + */ + public static String normalisePrefixes(String argsString) { + String[] parts = argsString.split(";"); + StringBuilder result = new StringBuilder(parts[0]); + + for (int i = 1; i < parts.length; i++) { + String part = parts[i]; + int colonIndex = part.indexOf(":"); + if (colonIndex != -1) { + String prefix = part.substring(0, colonIndex).trim(); + String value = part.substring(colonIndex + 1).trim(); + result.append(" ; ").append(prefix.toLowerCase()).append(" : ").append(value); + } else { + result.append(" ; ").append(part.trim()); + } + } + return result.toString(); + } + //@@author + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + public static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + //@@author yleeyilin + /** + * Returns name value using PREFIX. + * @param argMultimap Object that contains mapping of prefix to value. + * @param message Error message to throw if parse exception. + * @return Returns object representing name. + * @throws ParseException If command is in invalid format. + */ + public static Name mapName(ArgumentMultimap argMultimap, String message) throws ParseException { + try { + return ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + } catch (ParseException pe) { + throw new ParseException(String.format(message, pe.getMessage())); + } + } + //@@author + + //@@author yleeyilin + /** + * Returns field values using PREFIX. + * @param argMultimap Object that contains mapping of prefix to value. + * @param message Error message to throw if parse exception. + * @return Returns object representing the respective fields. + * @throws ParseException If command is in invalid format. + */ + public static String mapFields(ArgumentMultimap argMultimap, String message) throws ParseException { + try { + return " " + ParserUtil.parseField(argMultimap.getValue(PREFIX_FIELD).get()); + } catch (ParseException pe) { + throw new ParseException(message, pe); + } + } + //@@author + + //@@author Joshy837 + /** + * Returns sort field values using PREFIX. + * @param argMultimap Object that contains mapping of prefix to value. + * @param message Error message to throw if parse exception. + * @return Returns object representing the respective fields. + * @throws ParseException Thrown when command is in invalid format. + */ + public static Prefix mapSortFields(ArgumentMultimap argMultimap, String message) throws ParseException { + try { + String value = argMultimap.getValue(PREFIX_FIELD).get(); + + for (Prefix prefix : PREFIX_SORT_COLLECTION) { + String keyword = ParserUtil.parseSortField(prefix.getPrefix()); + if (keyword.equalsIgnoreCase(value)) { + return new Prefix(value); + } + } + + throw new ParseException(String.format(message)); + + } catch (ParseException pe) { + throw new ParseException(String.format(message, pe.getMessage())); + } + } + //@@author + + //@@author chiageng + /** + * Verifies that there are no invalid prefixes. + * @param args Arguments. + * @param message Error messages to be displayed. + * @param commandType Command Type. + * @param headerMessage Header of error messages. + * @param prefixes Required prefixes in the command. + * @throws ParseException If there are invalid prefixes. + */ + public static void verifyNoUnknownPrefix(String args, String message, String commandType, + String headerMessage, Prefix... prefixes) + throws ParseException { + ArrayList unknownPrefixes = ArgumentTokenizer.checkUnknownPrefix(args, + prefixes); + logger.log(Level.WARNING, "Parsing error while parsing for " + commandType + " command."); + if (unknownPrefixes.size() > 0) { + String exception = headerMessage + String.format(MESSAGE_UNKNOWN_FIELD_FORMAT, unknownPrefixes); + exception += "\n" + String.format(MESSAGE_COMMAND_FORMAT, message); + throw new ParseException(exception); + } + } + //@@author + + //@@author chiageng + /** + * Verifies that there are no missing prefixes. + * @param argMultimap Arguments. + * @param message Error messages to be displayed. + * @param commandType Command Type. + * @param headerMessage Header of error messages. + * @param prefixes Required prefixes in the command. + * @throws ParseException If there are missing prefixes. + */ + public static void verifyNoMissingField(ArgumentMultimap argMultimap, String message, String commandType, + String headerMessage, Prefix... prefixes) throws + ParseException { + if (!arePrefixesPresent(argMultimap, prefixes)) { + logger.log(Level.WARNING, "Parsing error while parsing for " + commandType + " command."); + ArrayList missingFields = ArgumentTokenizer.checkUndetectedPrefix(argMultimap, + prefixes); + String exception = headerMessage + String.format(MESSAGE_MISSING_FIELD_FORMAT, missingFields); + exception += "\n" + String.format(MESSAGE_COMMAND_FORMAT, message); + throw new ParseException(exception); + } + } + //@@author } diff --git a/src/main/java/seedu/address/logic/parser/PinCommandParser.java b/src/main/java/seedu/address/logic/parser/PinCommandParser.java new file mode 100644 index 00000000000..f883cf7c400 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/PinCommandParser.java @@ -0,0 +1,53 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.messages.PinMessages.FAILED_TO_PIN; +import static seedu.address.logic.messages.PinMessages.PIN; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.PinCommand; +import seedu.address.logic.messages.PinMessages; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Name; + +//@@author yleeyilin +/** + * Parses input arguments and creates a new PinCommand object. + */ +public class PinCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "Arguments to pass into pin command is null."; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for pin command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Parses the given {@code String} of arguments in the context of the PinCommand. + * and returns a PinCommand object for execution. Parameter {@code args} cannot be null. + * @throws ParseException If the user input does not conform to the expected format. + */ + public PinCommand parse(String args) throws ParseException { + requireNonNull(args); + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME); + + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(args, PinCommand.MESSAGE_USAGE, PIN, + FAILED_TO_PIN, + PREFIX_NAME); + ParserUtil.verifyNoMissingField(argMultimap, PinCommand.MESSAGE_USAGE, PIN, + FAILED_TO_PIN, + PREFIX_NAME); + + Name name = ParserUtil.mapName(argMultimap, PinMessages.MESSAGE_PIN_INVALID_NAME); + + return new PinCommand(name); + } +} diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/seedu/address/logic/parser/Prefix.java index 348b7686c8a..a6ff40d5815 100644 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ b/src/main/java/seedu/address/logic/parser/Prefix.java @@ -15,6 +15,14 @@ public String getPrefix() { return prefix; } + //@@author Joshy837 + public String getTrimmedPrefix() { + return prefix + .replace("; ", "") + .replace(" : ", ""); + } + //@@author + @Override public String toString() { return getPrefix(); diff --git a/src/main/java/seedu/address/logic/parser/RateCommandParser.java b/src/main/java/seedu/address/logic/parser/RateCommandParser.java new file mode 100644 index 00000000000..9bf73109bdc --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/RateCommandParser.java @@ -0,0 +1,61 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.messages.RateMessages.FAILED_TO_RATE; +import static seedu.address.logic.messages.RateMessages.RATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +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_NOTE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.RateCommand; +import seedu.address.logic.messages.RateMessages; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Name; +import seedu.address.model.person.Rating; + +//@@author jamessinmaojun +/** + * Parses input arguments and creates a new RateCommand object. + */ +public class RateCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "argument to pass for rate command is null"; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for rate command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Parses the given {@code String} of arguments in the context of the RateCommand. + * and returns a RateCommand object for execution. Parameter {@code args} cannot be null. + * @throws ParseException If the user input does not conform to the expected format. + */ + public RateCommand parse(String args) throws ParseException { + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_RATING); + + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(args, RateCommand.MESSAGE_USAGE, RATE, + FAILED_TO_RATE, + PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_NOTE, PREFIX_RATING); + ParserUtil.verifyNoMissingField(argMultimap, RateCommand.MESSAGE_USAGE, RATE, + FAILED_TO_RATE, + PREFIX_NAME, PREFIX_RATING); + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS); + + try { + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).orElseThrow()); + Rating rating = ParserUtil.parseRating(argMultimap.getValue(PREFIX_RATING).orElseThrow()); + return new RateCommand(name, rating); + } catch (ParseException pe) { + throw new ParseException(String.format(RateMessages.MESSAGE_RATE_INVALID_PARAMETERS, pe.getMessage())); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/SearchCommandParser.java b/src/main/java/seedu/address/logic/parser/SearchCommandParser.java new file mode 100644 index 00000000000..3b2187ec8c0 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/SearchCommandParser.java @@ -0,0 +1,67 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.messages.SearchMessages.FAILED_TO_SEARCH; +import static seedu.address.logic.messages.SearchMessages.SEARCH; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SEARCH_COLLECTION; + +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Stream; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.SearchCommand; +import seedu.address.logic.messages.SearchMessages; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.KeywordPredicate; + +//@@author Joshy837 +/** + * Parses input arguments and creates a new SearchCommand object. + */ +public class SearchCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "argument to pass for search command is null"; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for search command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Parses the given {@code String} of arguments in the context of the SearchCommand. + * and returns a SearchCommand object for execution. Parameter {@code args} cannot be null. + * @throws ParseException If the user input does not conform to the expected format. + */ + public SearchCommand parse(String args) throws ParseException { + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_SEARCH_COLLECTION); + + // validate correct user command + ParserUtil.verifyNoUnknownPrefix( + args, + SearchCommand.MESSAGE_USAGE, + SEARCH, + FAILED_TO_SEARCH, + PREFIX_SEARCH_COLLECTION); + boolean isPreambleEmpty = argMultimap.isPreambleEmpty(); + if (!isPreambleEmpty) { + throw new ParseException(SearchMessages.MESSAGE_SEARCH_INVALID_FIELD); + } + boolean isAnyPrefixPresent = atLeastOnePrefixPresent(argMultimap, PREFIX_SEARCH_COLLECTION); + if (!isAnyPrefixPresent) { + throw new ParseException(SearchMessages.MESSAGE_SEARCH_MISSING_FIELD); + } + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_SEARCH_COLLECTION); + argMultimap.verifyNoEmptyEntries(); + + return new SearchCommand(new KeywordPredicate(argMultimap)); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean atLeastOnePrefixPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).anyMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/SortCommandParser.java b/src/main/java/seedu/address/logic/parser/SortCommandParser.java new file mode 100644 index 00000000000..9197fa67282 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/SortCommandParser.java @@ -0,0 +1,50 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.messages.SortMessages.FAILED_TO_SORT; +import static seedu.address.logic.messages.SortMessages.SORT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_FIELD; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.SortCommand; +import seedu.address.logic.messages.SortMessages; +import seedu.address.logic.parser.exceptions.ParseException; + +//@@author Joshy837 +/** + * Parses input arguments and creates a new SortCommand object. + */ +public class SortCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "argument to pass for sort command is null"; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for sort command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + /** + * Parses the given {@code String} of arguments in the context of the SortCommand. + * and returns a SortCommand object for execution. Parameter {@code args} cannot be null. + * @throws ParseException If the user input does not conform to the expected format. + */ + public SortCommand parse(String args) throws ParseException { + requireNonNull(args); + + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_FIELD); + + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(args, SortCommand.MESSAGE_USAGE, SORT, + FAILED_TO_SORT, PREFIX_FIELD); + ParserUtil.verifyNoMissingField(argMultimap, SortCommand.MESSAGE_USAGE, SORT, + FAILED_TO_SORT, PREFIX_FIELD); + + Prefix prefix = ParserUtil.mapSortFields(argMultimap, SortMessages.MESSAGE_SORT_INVALID_FIELD); + + return new SortCommand(prefix); + } +} diff --git a/src/main/java/seedu/address/logic/parser/UnpinCommandParser.java b/src/main/java/seedu/address/logic/parser/UnpinCommandParser.java new file mode 100644 index 00000000000..e98eaeeef29 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/UnpinCommandParser.java @@ -0,0 +1,50 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.messages.UnpinMessages.FAILED_TO_UNPIN; +import static seedu.address.logic.messages.UnpinMessages.UNPIN; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.UnpinCommand; +import seedu.address.logic.messages.UnpinMessages; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Name; + +//@@author yleeyilin +/** + * Parses input arguments and creates a new UnpinCommand object. + */ +public class UnpinCommandParser implements Parser { + public static final String MESSAGE_NULL_ARGUMENTS = "Arguments to pass into unpin command is null."; + public static final String MESSAGE_COMMENCE_PARSING = "Going to start parsing for unpin command."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + /** + * Parses the given {@code String} of arguments in the context of the UnpinCommand. + * and returns an UnpinCommand object for execution. Parameter {@code args} cannot be null. + * @throws ParseException If the user input does not conform to the expected format. + */ + public UnpinCommand parse(String args) throws ParseException { + requireNonNull(args); + assert (args != null) : MESSAGE_NULL_ARGUMENTS; + + logger.log(Level.INFO, MESSAGE_COMMENCE_PARSING); + + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME); + + // validates user command fields + ParserUtil.verifyNoUnknownPrefix(args, UnpinCommand.MESSAGE_USAGE, UNPIN, + FAILED_TO_UNPIN, PREFIX_NAME); + ParserUtil.verifyNoMissingField(argMultimap, UnpinCommand.MESSAGE_USAGE, UNPIN, + FAILED_TO_UNPIN, PREFIX_NAME); + + Name name = ParserUtil.mapName(argMultimap, UnpinMessages.MESSAGE_UNPIN_INVALID_NAME); + + return new UnpinCommand(name); + } +} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index 73397161e84..77136fe93c9 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -6,6 +6,7 @@ import javafx.collections.ObservableList; import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.parser.Prefix; import seedu.address.model.person.Person; import seedu.address.model.person.UniquePersonList; @@ -94,6 +95,19 @@ public void removePerson(Person key) { persons.remove(key); } + //@@author yleeyilin + /** + * Sorts the contacts list so that pinned contacts will appear at the top of the list. + */ + public void updatePinnedList() { + persons.sortByPinnedStatus(); + } + //@@author + + public void updateSortedList(Prefix prefix) { + persons.sortBy(prefix); + } + //// util methods @Override diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index d54df471c1f..d8b397af551 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -5,7 +5,13 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.Prefix; +import seedu.address.model.person.Maintainer; +import seedu.address.model.person.Name; import seedu.address.model.person.Person; +import seedu.address.model.person.Staff; +import seedu.address.model.person.Supplier; /** * The API of the Model component. @@ -84,4 +90,92 @@ public interface Model { * @throws NullPointerException if {@code predicate} is null. */ void updateFilteredPersonList(Predicate predicate); + + /** + * Updates the filter of the filtered person list to filter by the given {@code predicate} with commit. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredPersonListWithCommit(Predicate predicate); + + /** + * Update the person list to display pinned contacts first. + */ + void updatePinnedPersonList(); + + /** + * Sorts the contact list by a specified parameter. + */ + void updateSortedPersonList(Prefix prefix); + + /** + * Find a general contact by their name. + * @param targetName Refers to the name identifier. + * @param message Refers to the exception message for the specific command. + * @return Contact that matches the name. + * @throws CommandException If no matching contact can be found. + */ + Person findByName(Name targetName, String message) throws CommandException; + + /** + * Find a person by their name. + * @param targetName Refers to the name identifier. + * @param message Refers to the exception message for the specific command. + * @return Person that matches the name. + * @throws CommandException If no valid person is found. + */ + Person findPersonByName(Name targetName, String message) throws CommandException; + + /** + * Find a maintainer by their name. + * @param targetName Refers to the name identifier. + * @param message Refers to the exception message for the specific command. + * @return Maintainer that matches the name. + * @throws CommandException If no valid maintainer is found. + */ + Maintainer findMaintainerByName(Name targetName, String message) throws CommandException; + + /** + * Find a staff by their name. + * @param targetName Refers to the name identifier. + * @param message Refers to the exception message for the specific command. + * @return Staff that matches the name. + * @throws CommandException If no valid staff is found. + */ + Staff findStaffByName(Name targetName, String message) throws CommandException; + + /** + * Find a supplier by their name. + * @param targetName Refers to the name identifier. + * @param message Refers to the exception message for the specific command. + * @return Supplier that matches the name. + * @throws CommandException If no valid supplier is found. + */ + Supplier findSupplierByName(Name targetName, String message) throws CommandException; + + /** + * Commits new version of AddressBook into VersionedAddressBook Tracker. + */ + void commitAddressBook(); + + /** + * Retrieves previous version of AddressBook from VersionedAddressBook Tracker. + */ + void undoAddressBook(); + + /** + * Retrieves future version of AddressBook due to undo from VerssionedAddressBook Tracker. + */ + void redoAddressBook(); + + /** + * Returns a boolean value to indicate whether undo command is possible to be carried out. + * @return True if possible to undo, else False. + */ + boolean canUndoAddressBook(); + + /** + * Returns a boolean value to indicate whether redo command is possible to be carried out. + * @return True if possible to redo, else False. + */ + boolean canRedoAddressBook(); } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 57bc563fde6..a8569bb6e64 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -11,7 +11,13 @@ import javafx.collections.transformation.FilteredList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.Prefix; +import seedu.address.model.person.Maintainer; +import seedu.address.model.person.Name; import seedu.address.model.person.Person; +import seedu.address.model.person.Staff; +import seedu.address.model.person.Supplier; /** * Represents the in-memory model of the address book data. @@ -19,10 +25,11 @@ public class ModelManager implements Model { private static final Logger logger = LogsCenter.getLogger(ModelManager.class); - private final AddressBook addressBook; + private final VersionedAddressBook addressBook; private final UserPrefs userPrefs; private final FilteredList filteredPersons; + //@@author chiageng /** * Initializes a ModelManager with the given addressBook and userPrefs. */ @@ -31,10 +38,11 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); - this.addressBook = new AddressBook(addressBook); + this.addressBook = new VersionedAddressBook(addressBook); this.userPrefs = new UserPrefs(userPrefs); filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); } + //@@author public ModelManager() { this(new AddressBook(), new UserPrefs()); @@ -80,6 +88,7 @@ public void setAddressBookFilePath(Path addressBookFilePath) { @Override public void setAddressBook(ReadOnlyAddressBook addressBook) { this.addressBook.resetData(addressBook); + commitAddressBook(); } @Override @@ -96,11 +105,13 @@ public boolean hasPerson(Person person) { @Override public void deletePerson(Person target) { addressBook.removePerson(target); + commitAddressBook(); } @Override public void addPerson(Person person) { addressBook.addPerson(person); + commitAddressBook(); updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); } @@ -111,6 +122,42 @@ public void setPerson(Person target, Person editedPerson) { addressBook.setPerson(target, editedPerson); } + //@@author chiageng + @Override + public void commitAddressBook() { + addressBook.commit(); + logger.fine("New commit on address book: " + addressBook + " and user prefs " + userPrefs); + } + + //@@author chiageng + @Override + public void undoAddressBook() { + addressBook.undo(); + logger.fine("Previous commit is retrieved to address book: " + + addressBook + " and user prefs " + userPrefs); + } + + //@@author chiageng + @Override + public void redoAddressBook() { + addressBook.redo(); + logger.fine("Recent commit is retrieved to address book: " + + addressBook + " and user prefs " + userPrefs); + } + + //@@author chiageng + @Override + public boolean canUndoAddressBook() { + return addressBook.canUndo(); + } + + //@@author chiageng + @Override + public boolean canRedoAddressBook() { + return addressBook.canRedo(); + } + //@@author + //=========== Filtered Person List Accessors ============================================================= /** @@ -128,6 +175,15 @@ public void updateFilteredPersonList(Predicate predicate) { filteredPersons.setPredicate(predicate); } + //@@author chiageng + @Override + public void updateFilteredPersonListWithCommit(Predicate predicate) { + requireNonNull(predicate); + filteredPersons.setPredicate(predicate); + commitAddressBook(); + } + //@@author + @Override public boolean equals(Object other) { if (other == this) { @@ -145,4 +201,124 @@ public boolean equals(Object other) { && filteredPersons.equals(otherModelManager.filteredPersons); } + //@@author yleeyilin + /** + * Update the person list to display pinned contacts first. + */ + public void updatePinnedPersonList() { + addressBook.updatePinnedList(); + commitAddressBook(); + } + //@@author + + /** + * Sorts the contact list by a specified parameter. + */ + public void updateSortedPersonList(Prefix prefix) { + addressBook.updateSortedList(prefix); + commitAddressBook(); + } + + //@@author yleeyilin + /** + * Find a general contact by their name. + * @param targetName Refers to the name identifier. + * @param message Refers to the exception message for the specific command. + * @return Contact that matches the name. + * @throws CommandException If no matching contact can be found. + */ + @Override + public Person findByName(Name targetName, String message) throws CommandException { + for (Person person: this.addressBook.getPersonList()) { + Name name = person.getName(); + if (name.equals(targetName)) { + return person; + } + } + throw new CommandException(message); + } + //@@author + + //@@author yleeyilin + /** + * Find a person by their name. + * @param targetName Refers to the name identifier. + * @param message Refers to the exception message for the specific command. + * @return Person that matches the name. + * @throws CommandException If no valid person is found. + */ + @Override + public Person findPersonByName(Name targetName, String message) throws CommandException { + for (Person person: this.addressBook.getPersonList()) { + Name name = person.getName(); + if (name.equals(targetName)) { + if (!(person instanceof Supplier) && !(person instanceof Staff) + && !(person instanceof Maintainer)) { + return person; + } + } + } + throw new CommandException(message); + } + //@@author + + //@@author yleeyilin + /** + * Find a maintainer by their name. + * @param targetName Refers to the name identifier. + * @param message Refers to the exception message for the specific command. + * @return Maintainer that matches the name. + * @throws CommandException If no valid maintainer is found. + */ + @Override + public Maintainer findMaintainerByName(Name targetName, String message) throws CommandException { + for (Person person: this.addressBook.getPersonList()) { + Name name = person.getName(); + if (name.equals(targetName) && person instanceof Maintainer) { + return (Maintainer) person; + } + } + throw new CommandException(message); + } + //@@author + + //@@author yleeyilin + /** + * Find a staff by their name. + * @param targetName Refers to the name identifier. + * @param message Refers to the exception message for the specific command. + * @return Staff that matches the name. + * @throws CommandException If no valid staff is found. + */ + @Override + public Staff findStaffByName(Name targetName, String message) throws CommandException { + for (Person person: this.addressBook.getPersonList()) { + Name name = person.getName(); + if (name.equals(targetName) && person instanceof Staff) { + return (Staff) person; + } + } + throw new CommandException(message); + } + //@@author + + //@@author yleeyilin + /** + * Find a supplier by their name. + * @param targetName Refers to the name identifier. + * @param message Refers to the exception message for the specific command. + * @return Supplier that matches the name. + * @throws CommandException If no valid supplier is found. + */ + @Override + public Supplier findSupplierByName(Name targetName, String message) throws CommandException { + for (Person person: this.addressBook.getPersonList()) { + Name name = person.getName(); + if (name.equals(targetName) && person instanceof Supplier) { + return (Supplier) person; + } + } + throw new CommandException(message); + } + //@@author } diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java index 6be655fb4c7..de4f8fb1e15 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" , "poochplanner.json"); /** * Creates a {@code UserPrefs} with default values. diff --git a/src/main/java/seedu/address/model/VersionedAddressBook.java b/src/main/java/seedu/address/model/VersionedAddressBook.java new file mode 100644 index 00000000000..36daa585b8f --- /dev/null +++ b/src/main/java/seedu/address/model/VersionedAddressBook.java @@ -0,0 +1,110 @@ +package seedu.address.model; + +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; + +//@@author chiageng +/** + * Extended version of Address Book storing History of Address Book. + */ +public class VersionedAddressBook extends AddressBook { + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + private ArrayList addressBookStateList; + private int currentStatePointer; + + /** + * Creates VersionedAddressBook and initialize addressBookStateList to track history. + * @param addressBook initial address book + */ + public VersionedAddressBook(ReadOnlyAddressBook addressBook) { + super(addressBook); + + addressBookStateList = new ArrayList<>(); + addressBookStateList.add(new AddressBook(addressBook)); + currentStatePointer = 0; + } + + /** + * Commits the new state of address book into tracker addressBookStateList. + */ + public void commit() { + updateSubAddressBookStateList(); + this.addressBookStateList.add(new AddressBook(this)); + currentStatePointer += 1; + } + + /** + * Reverts the address book to its previous state. + */ + public void undo() { + currentStatePointer -= 1; + resetData(addressBookStateList.get(currentStatePointer)); + } + + /** + * Checks whether the address book can be reverted to a previous state. + * @return true if there is a previous state to revert to, else false. + */ + public boolean canUndo() { + if (currentStatePointer <= 0) { + // cannot undo, no more address book behind the list. + logger.log(Level.WARNING, "No more previous state, unable to undo."); + return false; + } + return true; + } + + /** + * Advances the address book to its next state if possible. + */ + public void redo() { + currentStatePointer += 1; + resetData(addressBookStateList.get(currentStatePointer)); + } + + /** + * Checks whether the address book can be advanced to a next state. + * @return true if here is a next state to advance to, else false. + */ + public boolean canRedo() { + if (currentStatePointer >= addressBookStateList.size() - 1) { + // cannot redo, no more address book in front of list. + logger.log(Level.WARNING, "No more undo state, unable to redo."); + return false; + } + return true; + } + + /** + * Updates the sublist of address book states up to the current state pointer. + */ + private void updateSubAddressBookStateList() { + ArrayList copy = new ArrayList<>(); + + for (int i = 0; i <= currentStatePointer; i++) { + copy.add(addressBookStateList.get(i)); + } + + this.addressBookStateList = copy; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof VersionedAddressBook)) { + return false; + } + + VersionedAddressBook otherAddressBook = (VersionedAddressBook) other; + return super.equals(otherAddressBook) + && addressBookStateList.equals(otherAddressBook.addressBookStateList) + && currentStatePointer == otherAddressBook.currentStatePointer; + } +} diff --git a/src/main/java/seedu/address/model/person/Commission.java b/src/main/java/seedu/address/model/person/Commission.java new file mode 100644 index 00000000000..906acddcd85 --- /dev/null +++ b/src/main/java/seedu/address/model/person/Commission.java @@ -0,0 +1,59 @@ +package seedu.address.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +//@@author chiageng +/** + * Represents a Maitainer's commission in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidCommission(String)} + */ +public class Commission { + public static final String MESSAGE_CONSTRAINTS = + "Commision should be in this format of ${amount}/hr"; + public static final String VALIDATION_REGEX = "^\\$\\d+/hr$"; + public final String value; + + /** + * Constructs a {@code Commission}. + * + * @param value A valid commission. + */ + public Commission(String value) { + requireNonNull(value); + checkArgument(isValidCommission(value), MESSAGE_CONSTRAINTS); + this.value = value; + } + + /** + * Returns true if a given string is a valid commission. + */ + public static boolean isValidCommission(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Commission)) { + return false; + } + + Commission otherCommission = (Commission) other; + return value.equals(otherCommission.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/person/DeadlineNote.java b/src/main/java/seedu/address/model/person/DeadlineNote.java new file mode 100644 index 00000000000..e89fcfaab93 --- /dev/null +++ b/src/main/java/seedu/address/model/person/DeadlineNote.java @@ -0,0 +1,98 @@ +package seedu.address.model.person; + +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.util.Objects; + +//@@author jannaleong +/** + * Represents a Person's note with a deadline in the address book. + */ +public class DeadlineNote extends Note { + + public static final String MESSAGE_INVALID_DATE = "Date must be in the format yyyy-mm-dd (e.g., 2019-10-15)"; + private String deadline; + + /** + * Constructs a {@code DeadlineNote}. + * + * @param note A note value. + * @param deadline A deadline value. + */ + public DeadlineNote(String note, String deadline) { + super(note); + requireNonNull(note); + checkArgument(isValidNote(note), MESSAGE_CONSTRAINTS); + this.deadline = convertDate(deadline); + } + + /** + * Returns true if date is in valid format, else + * returns false. + * + * @param test Date to test. + * @return Boolean indicating if date is in the + * correct format. + */ + public static boolean isValidDate(String test) { + if (test.equals("")) { + return false; + } + if (!test.matches(VALIDATION_REGEX)) { + return false; + } + try { + LocalDate.parse(test); + return true; + } catch (DateTimeParseException e) { + // don't change the date in this case + // as it is not a valid date, return false. + } + return false; + } + + /** + * Returns date converted into a more + * readable format. + * + * @param deadline Deadline to convert. + * @return Converted deadline. + */ + public String convertDate(String deadline) { + assert (isValidDate(deadline)) : "deadline to convert is not valid"; + + LocalDate originalDate = LocalDate.parse(deadline); + return originalDate.format(DateTimeFormatter.ofPattern("MMM d yyyy")); + } + + + @Override + public String toString() { + return super.toString() + " by: " + this.deadline; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof DeadlineNote)) { + return false; + } + + DeadlineNote otherNote = (DeadlineNote) other; + boolean isDeadlineEqual = deadline.equals(otherNote.deadline); + return super.equals(otherNote) && isDeadlineEqual; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), deadline); + } +} diff --git a/src/main/java/seedu/address/model/person/Employment.java b/src/main/java/seedu/address/model/person/Employment.java new file mode 100644 index 00000000000..5dc97c807e1 --- /dev/null +++ b/src/main/java/seedu/address/model/person/Employment.java @@ -0,0 +1,60 @@ +package seedu.address.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +//@@author chiageng +/** + * Represents a Staff's employment status in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidEmployment(String)} )} + */ +public class Employment { + public static final String MESSAGE_CONSTRAINTS = + "Employment should be either full-time or part-time"; + public static final String VALIDATION_REGEX1 = "part-time"; + public static final String VALIDATION_REGEX2 = "full-time"; + public final String value; + + /** + * Constructs an {@code Employment}. + * + * @param value A valid employment either part-time or full-time. + */ + public Employment(String value) { + requireNonNull(value); + checkArgument(isValidEmployment(value), MESSAGE_CONSTRAINTS); + this.value = value; + } + + /** + * Returns true if a given string is a valid salary. + */ + public static boolean isValidEmployment(String test) { + return test.matches(VALIDATION_REGEX1) || test.matches(VALIDATION_REGEX2); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Employment)) { + return false; + } + + Employment otherEmployment = (Employment) other; + return value.equals(otherEmployment.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/person/KeywordPredicate.java b/src/main/java/seedu/address/model/person/KeywordPredicate.java new file mode 100644 index 00000000000..27560715d61 --- /dev/null +++ b/src/main/java/seedu/address/model/person/KeywordPredicate.java @@ -0,0 +1,159 @@ +package seedu.address.model.person; + +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMMISSION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMPLOYMENT; +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_PRICE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PRODUCT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SKILL; + +import java.util.function.Predicate; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.Prefix; + +//@@author Joshy837 +/** + * Tests that a {@code Person}'s {@code Details} matches any of the keywords given. + */ +public class KeywordPredicate implements Predicate { + private final ArgumentMultimap keywords; + + public KeywordPredicate(ArgumentMultimap keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Person person) { + + // Searches through staff only + if (hasField(PREFIX_SALARY) || hasField(PREFIX_EMPLOYMENT)) { + if (!(person instanceof Staff)) { + return false; + } + + Staff staff = (Staff) person; + return checkNamePhoneEmailAndAddress(staff) + && checkSalary(staff) + && checkEmployment(staff); + } + + // Searches through supplier only + if (hasField(PREFIX_PRICE) || hasField(PREFIX_PRODUCT)) { + if (!(person instanceof Supplier)) { + return false; + } + + Supplier supplier = (Supplier) person; + return checkNamePhoneEmailAndAddress(supplier) + && checkPrice(supplier) + && checkProduct(supplier); + } + + // Searches through maintainer only + if (hasField(PREFIX_SKILL) || hasField(PREFIX_COMMISSION)) { + if (!(person instanceof Maintainer)) { + return false; + } + + Maintainer maintainer = (Maintainer) person; + return checkNamePhoneEmailAndAddress(maintainer) + && checkSkill(maintainer) + && checkCommission(maintainer); + } + + // No specialty + return checkNamePhoneEmailAndAddress(person); + } + + boolean hasField(Prefix field) { + return keywords.getValue(field).isPresent(); + } + + String getValue(Prefix field) { + return keywords.getValue(field).get(); + } + + boolean contains(String identifier, Prefix field) { + if (!hasField(field)) { + return true; + } + + String lowerCasedIdentifier = identifier.toLowerCase(); + String query = getValue(field); + String lowerCasedQuery = query.toLowerCase(); + return lowerCasedIdentifier.contains(lowerCasedQuery); + } + + boolean checkName(Person person) { + return contains(person.getName().toString(), PREFIX_NAME); + } + + boolean checkPhone(Person person) { + return contains(person.getPhone().toString(), PREFIX_PHONE); + } + + boolean checkEmail(Person person) { + return contains(person.getEmail().toString(), PREFIX_EMAIL); + } + + boolean checkAddress(Person person) { + return contains(person.getAddress().toString(), PREFIX_ADDRESS); + } + + boolean checkSalary(Staff staff) { + return contains(staff.getSalary().toString(), PREFIX_SALARY); + } + + boolean checkEmployment(Staff staff) { + return contains(staff.getEmployment().toString(), PREFIX_EMPLOYMENT); + } + + boolean checkPrice(Supplier supplier) { + return contains(supplier.getPrice().toString(), PREFIX_PRICE); + } + + boolean checkProduct(Supplier supplier) { + return contains(supplier.getProduct().toString(), PREFIX_PRODUCT); + } + + boolean checkSkill(Maintainer maintainer) { + return contains(maintainer.getSkill().toString(), PREFIX_SKILL); + } + + boolean checkCommission(Maintainer maintainer) { + return contains(maintainer.getCommission().toString(), PREFIX_COMMISSION); + } + + boolean checkNamePhoneEmailAndAddress(Person person) { + return checkName(person) + && checkPhone(person) + && checkEmail(person) + && checkAddress(person); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof KeywordPredicate)) { + return false; + } + + KeywordPredicate otherKeywordPredicate = (KeywordPredicate) other; + return keywords.equals(otherKeywordPredicate.keywords); + } + + @Override + public String toString() { + return new ToStringBuilder(this).add("keyword", keywords).toString(); + } +} diff --git a/src/main/java/seedu/address/model/person/Maintainer.java b/src/main/java/seedu/address/model/person/Maintainer.java new file mode 100644 index 00000000000..75ce4f440a6 --- /dev/null +++ b/src/main/java/seedu/address/model/person/Maintainer.java @@ -0,0 +1,147 @@ +package seedu.address.model.person; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Objects; +import java.util.Set; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.model.tag.Tag; + +//@@author chiageng +/** + * Represents a Maintainer in the address book. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Maintainer extends Person { + // Data fields + private final Skill skill; + private final Commission commission; + + /** + * Every field must be present and not null. + */ + public Maintainer(Name name, Phone phone, Email email, Address address, Note note, Set tags, + Skill skill, Commission commission, Rating rating) { + super(name, phone, email, address, note, tags, rating); + requireAllNonNull(skill, commission); + this.skill = skill; + this.commission = commission; + } + //@@author + + //@@author chiageng + public Skill getSkill() { + return skill; + } + //@@author + + //@@author chiageng + public Commission getCommission() { + return commission; + } + //@@author + + //@@author jannaleong + /** + * Returns a new instantiation of the current {@code Maintainer} with the updated note, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + @Override + public Maintainer updateNote(Note note) { + Maintainer maintainerToReturn = new Maintainer(this.getName(), this.getPhone(), this.getEmail(), + this.getAddress(), note, this.getTags(), this.skill, this.commission, this.getRating()); + maintainerToReturn.setPinIfPinned(this); + return maintainerToReturn; + } + //@@author + + //@@author jamessinmaojun + /** + * Returns a new instantiation of the current {@code Maintainer} with the updated rating, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + @Override + public Maintainer updateRating(Rating rating) { + Maintainer maintainerToReturn = new Maintainer(this.getName(), this.getPhone(), this.getEmail(), + this.getAddress(), this.getNote(), this.getTags(), this.skill, this.commission, rating); + maintainerToReturn.setPinIfPinned(this); + return maintainerToReturn; + } + //@@author + + //@@author yleeyilin + /** + * Returns a new instantiation of the current {@code Maintainer} with the updated pin, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + @Override + public Maintainer updateToPinned() { + Maintainer maintainerToReturn = new Maintainer(this.getName(), this.getPhone(), this.getEmail(), + this.getAddress(), this.getNote(), this.getTags(), this.skill, this.commission, this.getRating()); + maintainerToReturn.toPin(); + return maintainerToReturn; + } + //@@author + + //@@author yleeyilin + /** + * Returns a new instantiation of the current {@code Maintainer} with the updated unpin, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + @Override + public Maintainer updateToUnpinned() { + Maintainer maintainerToReturn = new Maintainer(this.getName(), this.getPhone(), this.getEmail(), + this.getAddress(), this.getNote(), this.getTags(), this.skill, this.commission, this.getRating()); + maintainerToReturn.toUnpin(); + return maintainerToReturn; + } + //@@author + + //@@author chiageng + /** + * Returns true if both Maintainer have the same identity and data fields. + * This defines a stronger notion of equality between two maintainer. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Maintainer)) { + return false; + } + + Maintainer otherPerson = (Maintainer) other; + return super.equals(otherPerson) + && skill.equals(otherPerson.skill) + && commission.equals(otherPerson.commission); + } + //@@author + + //@@author chiageng + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(super.hashCode(), skill, commission); + } + //@@author + + //@@author chiageng + @Override + public String toString() { + return new ToStringBuilder(this) + .add("name", getName()) + .add("phone", getPhone()) + .add("email", getEmail()) + .add("address", getAddress()) + .add("tags", getTags()) + .add("rating", getRating()) + .add("skill", skill) + .add("commission", commission) + .toString(); + } + //@@author +} diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/person/Name.java index 173f15b9b00..70f29c100f7 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/seedu/address/model/person/Name.java @@ -10,13 +10,14 @@ public class Name { public static final String MESSAGE_CONSTRAINTS = - "Names should only contain alphanumeric characters and spaces, and it should not be blank"; + "Names should only contain alphanumeric characters, spaces and '/', and it should not be blank"; /* * 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 = "[\\p{Alnum}][\\p{Alnum} ]*"; + public static final String VALIDATION_REGEX = "[\\p{Alnum}/][\\p{Alnum}/ ]*"; + public final String fullName; @@ -56,7 +57,7 @@ public boolean equals(Object other) { } Name otherName = (Name) other; - return fullName.equals(otherName.fullName); + return fullName.toLowerCase().equals(otherName.fullName.toLowerCase()); } @Override diff --git a/src/main/java/seedu/address/model/person/Note.java b/src/main/java/seedu/address/model/person/Note.java new file mode 100644 index 00000000000..0642a2a35ed --- /dev/null +++ b/src/main/java/seedu/address/model/person/Note.java @@ -0,0 +1,77 @@ +package seedu.address.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +//@@author jannaleong +/** + * Represents a Person's Note in the address book. + */ +public class Note { + public static final String MESSAGE_CONSTRAINTS = "Note can take any values, and it should not be blank"; + public static final String VALIDATION_REGEX = "[^\\s].*"; + + private String value; + + /** + * Constructs a {@code Note}. + * + * @param note A note value. + */ + public Note(String note) { + requireNonNull(note); + checkArgument(isValidNote(note), MESSAGE_CONSTRAINTS); + value = note; + } + + + /** + * Returns true if a given string is a valid note. + * + * @param test Note to test. + * @return Boolean indicating whether the string is a valid note. + */ + public static boolean isValidNote(String test) { + return test.matches(VALIDATION_REGEX); + } + + /** + * Returns true if a given string contains the deadline prefix. + * + * @param test String to test. + * @return Boolean indicating whether the string contains the deadline prefix. + */ + public static boolean isNoteContainingDeadline(String test) { + String trimmedDeadlinePrefix = "; deadline :"; + return test.contains(trimmedDeadlinePrefix); + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Note)) { + return false; + } + + Note otherNote = (Note) other; + return value.equals(otherNote.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java index abe8c46b535..bf4961e5f88 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/seedu/address/model/person/Person.java @@ -24,17 +24,23 @@ public class Person { // Data fields private final Address address; private final Set tags = new HashSet<>(); + private final Note note; + private final Rating rating; + private Pin pin; /** * 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); + public Person(Name name, Phone phone, Email email, Address address, Note note, Set tags, Rating rating) { + requireAllNonNull(name, phone, email, address, note, tags, rating); this.name = name; this.phone = phone; this.email = email; this.address = address; + this.note = note; + this.pin = new Pin(); this.tags.addAll(tags); + this.rating = rating; } public Name getName() { @@ -53,6 +59,89 @@ public Address getAddress() { return address; } + public Note getNote() { + return note; + } + public Rating getRating() { + return rating; + } + + //@@author jannaleong + /** + * Returns a new instantiation of the current {@code Person} with the updated note, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + public Person updateNote(Note note) { + Person personToReturn = new Person(this.name, this.phone, this.email, this.address, note, this.tags, + this.rating); + personToReturn.setPinIfPinned(this); + return personToReturn; + } + //@@author + + //@@author jamessinmaojun + /** + * Returns a new instantiation of the current {@code Person} with the updated rating, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + public Person updateRating(Rating rating) { + Person personToReturn = new Person(this.name, this.phone, this.email, this.address, this.note, this.tags, + rating); + personToReturn.setPinIfPinned(this); + return personToReturn; + } + //@@author + + //@@author yleeyilin + /** + * Returns a new instantiation of the current {@code Person} with the updated pin, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + public Person updateToPinned() { + Person personToReturn = new Person(this.name, this.phone, this.email, this.address, this.note, this.tags, + this.rating); + personToReturn.toPin(); + return personToReturn; + } + //@@author + + //@@author yleeyilin + /** + * Returns a new instantiation of the current {@code Person} with the updated unpin, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + public Person updateToUnpinned() { + Person personToReturn = new Person(this.name, this.phone, this.email, this.address, this.note, this.tags, + this.rating); + personToReturn.toUnpin(); + return personToReturn; + } + //@@author + + //@@author yleeyilin + public Pin getPin() { + return this.pin; + } + + public void toPin() { + pin.setPin(); + } + + public void toUnpin() { + pin.setUnpin(); + } + + public boolean isPinned() { + return pin.getIsPinned(); + } + + public void setPinIfPinned(Person person) { + if (person.isPinned()) { + this.toPin(); + } + } + //@@author + /** * Returns an immutable tag set, which throws {@code UnsupportedOperationException} * if modification is attempted. @@ -63,6 +152,7 @@ public Set getTags() { /** * Returns true if both persons have the same name. + * Name is case-insensitive, Janna and janna is same name. * This defines a weaker notion of equality between two persons. */ public boolean isSamePerson(Person otherPerson) { @@ -74,6 +164,12 @@ public boolean isSamePerson(Person otherPerson) { && otherPerson.getName().equals(getName()); } + //@@author jannaleong + public void setNoteContent(String content) { + this.note.setValue(content); + } + //@@author + /** * Returns true if both persons have the same identity and data fields. * This defines a stronger notion of equality between two persons. @@ -100,7 +196,7 @@ public boolean equals(Object other) { @Override public int hashCode() { // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); + return Objects.hash(name, phone, email, address, note, tags, rating); } @Override @@ -110,8 +206,9 @@ public String toString() { .add("phone", phone) .add("email", email) .add("address", address) + .add("note", note) .add("tags", tags) + .add("rating", rating) .toString(); } - } diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/person/Phone.java index d733f63d739..e5f215a2993 100644 --- a/src/main/java/seedu/address/model/person/Phone.java +++ b/src/main/java/seedu/address/model/person/Phone.java @@ -3,13 +3,12 @@ import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; +//@@author chiageng /** * Represents a Person's phone number in the address book. * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} */ public class Phone { - - public static final String MESSAGE_CONSTRAINTS = "Phone numbers should only contain numbers, and it should be at least 3 digits long"; public static final String VALIDATION_REGEX = "\\d{3,}"; diff --git a/src/main/java/seedu/address/model/person/Pin.java b/src/main/java/seedu/address/model/person/Pin.java new file mode 100644 index 00000000000..b60af43d7b1 --- /dev/null +++ b/src/main/java/seedu/address/model/person/Pin.java @@ -0,0 +1,54 @@ +package seedu.address.model.person; + +//@@author yleeyilin +/** + * Represents a Person's Pin in the address book. + */ +public class Pin { + + private Boolean isPin; + + /** + * Constructs a {@code Pin}. + */ + public Pin() { + isPin = false; + } + + public void setPin() { + this.isPin = true; + } + + public void setUnpin() { + this.isPin = false; + } + + public boolean getIsPinned() { + return this.isPin; + } + + @Override + public String toString() { + return isPin ? "true" : "false"; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Note)) { + return false; + } + + Pin otherPin = (Pin) other; + return isPin.equals(otherPin.isPin); + } + + @Override + public int hashCode() { + return isPin.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/person/Price.java b/src/main/java/seedu/address/model/person/Price.java new file mode 100644 index 00000000000..eedd2c42687 --- /dev/null +++ b/src/main/java/seedu/address/model/person/Price.java @@ -0,0 +1,59 @@ +package seedu.address.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +//@@author chiageng +/** + * Represents a Supplier's product price in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidPrice(String)} + */ +public class Price { + public static final String MESSAGE_CONSTRAINTS = + "Price should be in this format of ${amount}/{quantity}"; + public static final String VALIDATION_REGEX = "^\\$\\d+/.+$"; + public final String value; + + /** + * Constructs a {@code Price}. + * + * @param value A valid price. + */ + public Price(String value) { + requireNonNull(value); + checkArgument(isValidPrice(value), MESSAGE_CONSTRAINTS); + this.value = value; + } + + /** + * Returns true if a given string is a valid salary. + */ + public static boolean isValidPrice(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Price)) { + return false; + } + + Price otherPrice = (Price) other; + return value.equals(otherPrice.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/person/Product.java b/src/main/java/seedu/address/model/person/Product.java new file mode 100644 index 00000000000..0180c027107 --- /dev/null +++ b/src/main/java/seedu/address/model/person/Product.java @@ -0,0 +1,63 @@ +package seedu.address.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +//@@author chiageng +/** + * Represents a Supplier's product in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidProduct(String)} + */ +public class Product { + public static final String MESSAGE_CONSTRAINTS = "Product can take any values, and it should not be blank"; + + /* + * The first character of the product 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 Product}. + * + * @param value A valid product. + */ + public Product(String value) { + requireNonNull(value); + checkArgument(isValidProduct(value), MESSAGE_CONSTRAINTS); + this.value = value; + } + + /** + * Returns true if a given string is a valid product. + */ + public static boolean isValidProduct(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Product)) { + return false; + } + + Product otherProduct = (Product) other; + return value.equals(otherProduct.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/person/Rating.java b/src/main/java/seedu/address/model/person/Rating.java new file mode 100644 index 00000000000..053bd211f3b --- /dev/null +++ b/src/main/java/seedu/address/model/person/Rating.java @@ -0,0 +1,62 @@ +package seedu.address.model.person; + +//@@author jamessinmaojun +/** + * Represents a Person's Rating in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidRating(String)} + */ +public class Rating { + public static final String MESSAGE_CONSTRAINTS = "Rating must be an integer between 1 and 5 inclusive."; + + /* + * Ratings must be integers between 1 and 5 inclusive. A 0 rating is unrated + */ + public static final String VALIDATION_REGEX = "[0-5]"; + + private String value; + + /** + * Constructs a {@code Rating}. + * + * @param rating A valid rating. + */ + public Rating(String rating) { + value = rating; + } + + /** + * Returns true if a given string is a valid email. + */ + public static boolean isValidRating(String test) { + return test.matches(VALIDATION_REGEX); + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Note)) { + return false; + } + + Rating otherRating = (Rating) other; + return value.equals(otherRating.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/person/RemindPredicate.java b/src/main/java/seedu/address/model/person/RemindPredicate.java new file mode 100644 index 00000000000..34e8ce32b7c --- /dev/null +++ b/src/main/java/seedu/address/model/person/RemindPredicate.java @@ -0,0 +1,61 @@ +package seedu.address.model.person; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.function.Predicate; + +import seedu.address.commons.util.ToStringBuilder; + +//@@author jannaleong +/** + * Tests that a {@code Person}'s Note deadline is today or after today. + */ +public class RemindPredicate implements Predicate { + @Override + public boolean test(Person person) { + Note currNote = person.getNote(); + + // checks if note is a deadline note by checking if it contains + // the string " by: " + if (!currNote.toString().contains(" by: ")) { + return false; + } + + String[] noteAndDate = currNote.toString().split(" by: "); + try { + DateTimeFormatter inputFormat = DateTimeFormatter.ofPattern("MMM d yyyy"); + LocalDate convertedDate = LocalDate.parse(noteAndDate[1], inputFormat); + String outputDate = convertedDate.toString(); + LocalDate noteDate = LocalDate.parse(outputDate); + LocalDate currDate = LocalDate.now(); + + if (!noteDate.isBefore(currDate)) { + return true; + } else { + return false; + } + } catch (DateTimeException e) { + // don't change the date in this case + // as it is not a valid date, return false. + } + return false; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + // instanceof handles nulls + if (!(other instanceof RemindPredicate)) { + return false; + } + return true; + } + + @Override + public String toString() { + return new ToStringBuilder(this).toString(); + } +} diff --git a/src/main/java/seedu/address/model/person/Salary.java b/src/main/java/seedu/address/model/person/Salary.java new file mode 100644 index 00000000000..f5bef673105 --- /dev/null +++ b/src/main/java/seedu/address/model/person/Salary.java @@ -0,0 +1,59 @@ +package seedu.address.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +//@@author chiageng +/** + * Represents a Staff's salary in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidSalary(String)} + */ +public class Salary { + public static final String MESSAGE_CONSTRAINTS = + "Salary should be in this format of ${amount}/hr"; + public static final String VALIDATION_REGEX = "^\\$\\d+/hr$"; + public final String value; + + /** + * Constructs a {@code Salary}. + * + * @param value A valid salary value. + */ + public Salary(String value) { + requireNonNull(value); + checkArgument(isValidSalary(value), MESSAGE_CONSTRAINTS); + this.value = value; + } + + /** + * Returns true if a given string is a valid salary. + */ + public static boolean isValidSalary(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Salary)) { + return false; + } + + Salary otherSalary = (Salary) other; + return value.equals(otherSalary.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/person/Skill.java b/src/main/java/seedu/address/model/person/Skill.java new file mode 100644 index 00000000000..dd50c010c03 --- /dev/null +++ b/src/main/java/seedu/address/model/person/Skill.java @@ -0,0 +1,63 @@ +package seedu.address.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +//@@author chiageng +/** + * Represents a Maintainer's skill in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidSkill(String)} + */ +public class Skill { + public static final String MESSAGE_CONSTRAINTS = "Skill can take any values, and it should not be blank"; + + /* + * The first character of the skill 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 Skill}. + * + * @param value A valid skill. + */ + public Skill(String value) { + requireNonNull(value); + checkArgument(isValidSkill(value), MESSAGE_CONSTRAINTS); + this.value = value; + } + + /** + * Returns true if a given string is a valid product. + */ + public static boolean isValidSkill(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Skill)) { + return false; + } + + Skill otherSkill = (Skill) other; + return value.equals(otherSkill.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/person/Staff.java b/src/main/java/seedu/address/model/person/Staff.java new file mode 100644 index 00000000000..a3336d875a6 --- /dev/null +++ b/src/main/java/seedu/address/model/person/Staff.java @@ -0,0 +1,144 @@ +package seedu.address.model.person; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Objects; +import java.util.Set; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.model.tag.Tag; + +//@@author chiageng +/** + * Represents a Staff in the address book. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Staff extends Person { + // Data fields + private final Salary salary; + private final Employment employment; + + /** + * Every field must be present and not null. + */ + public Staff(Name name, Phone phone, Email email, Address address, Note note, Set tags, + Salary salary, Employment employment, Rating rating) { + super(name, phone, email, address, note, tags, rating); + requireAllNonNull(salary, employment); + this.salary = salary; + this.employment = employment; + } + //@@author + + //@@author chiageng + public Salary getSalary() { + return salary; + } + //@@author + + //@@author chiageng + public Employment getEmployment() { + return employment; + } + //@@author + + //@@author jannaleong + /** + * Returns a new instantiation of the current {@code Staff} with the updated note, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + @Override + public Staff updateNote(Note note) { + Staff staffToReturn = new Staff(this.getName(), this.getPhone(), this.getEmail(), this.getAddress(), note, + this.getTags(), this.salary, this.employment, this.getRating()); + staffToReturn.setPinIfPinned(this); + return staffToReturn; + } + //@@author + + //@@ jamessinmaojun + /** + * Returns a new instantiation of the current {@code Staff} with the updated rating, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + @Override + public Staff updateRating(Rating rating) { + Staff staffToReturn = new Staff(this.getName(), this.getPhone(), this.getEmail(), this.getAddress(), + this.getNote(), this.getTags(), this.salary, this.employment, rating); + staffToReturn.setPinIfPinned(this); + return staffToReturn; + } + //@@author + + //@@author yleeyilin + /** + * Returns a new instantiation of the current {@code Staff} with the updated pin, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + @Override + public Staff updateToPinned() { + Staff staffToReturn = new Staff(this.getName(), this.getPhone(), this.getEmail(), this.getAddress(), + this.getNote(), this.getTags(), this.salary, this.employment, this.getRating()); + staffToReturn.toPin(); + return staffToReturn; + } + //@@author + + //@@author yleeyilin + /** + * Returns a new instantiation of the current {@code Staff} with the updated unpin, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + @Override + public Staff updateToUnpinned() { + Staff staffToReturn = new Staff(this.getName(), this.getPhone(), this.getEmail(), this.getAddress(), + this.getNote(), this.getTags(), this.salary, this.employment, this.getRating()); + staffToReturn.toUnpin(); + return staffToReturn; + } + //@@author + + //@@author chiageng + /** + * Returns true if both staffs have the same identity and data fields. + * This defines a stronger notion of equality between two staff. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Staff)) { + return false; + } + + Staff otherPerson = (Staff) other; + return super.equals(otherPerson) + && salary.equals(otherPerson.salary) + && employment.equals(otherPerson.employment); + } + + //@@author chiageng + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(super.hashCode(), salary, employment); + } + + //@@author chiageng + @Override + public String toString() { + return new ToStringBuilder(this) + .add("name", getName()) + .add("phone", getPhone()) + .add("email", getEmail()) + .add("address", getAddress()) + .add("tags", getTags()) + .add("rating", getRating()) + .add("salary", salary) + .add("employment", employment) + .toString(); + } +} diff --git a/src/main/java/seedu/address/model/person/Supplier.java b/src/main/java/seedu/address/model/person/Supplier.java new file mode 100644 index 00000000000..62c40409832 --- /dev/null +++ b/src/main/java/seedu/address/model/person/Supplier.java @@ -0,0 +1,144 @@ +package seedu.address.model.person; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Objects; +import java.util.Set; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.model.tag.Tag; + +//@@author chiageng +/** + * Represents a Supplier in the address book. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Supplier extends Person { + // Data fields + private final Product product; + private final Price price; + + /** + * Every field must be present and not null. + */ + public Supplier(Name name, Phone phone, Email email, Address address, Note note, Set tags, + Product product, Price price, Rating rating) { + super(name, phone, email, address, note, tags, rating); + requireAllNonNull(product, price); + this.product = product; + this.price = price; + } + //@@author + + //@@author chiageng + public Price getPrice() { + return price; + } + //@@author + + //@@author chiageng + public Product getProduct() { + return product; + } + //@@author + + //@@author jannaleong + /** + * Returns a new instantiation of the current {@code Supplier} with the updated note, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + @Override + public Supplier updateNote(Note note) { + Supplier supplierToReturn = new Supplier(this.getName(), this.getPhone(), this.getEmail(), + this.getAddress(), note, this.getTags(), this.product, this.price, this.getRating()); + supplierToReturn.setPinIfPinned(this); + return supplierToReturn; + } + //@@author + + //@@author jamessinmaojun + /** + * Returns a new instantiation of the current {@code Supplier} with the updated rating, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + @Override + public Supplier updateRating(Rating rating) { + Supplier supplierToReturn = new Supplier(this.getName(), this.getPhone(), this.getEmail(), this.getAddress(), + this.getNote(), this.getTags(), this.product, this.price, rating); + supplierToReturn.setPinIfPinned(this); + return supplierToReturn; + } + //@@author + + //@@author yleeyilin + /** + * Returns a new instantiation of the current {@code Supplier} with the updated pin, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + @Override + public Supplier updateToPinned() { + Supplier supplierToReturn = new Supplier(this.getName(), this.getPhone(), this.getEmail(), this.getAddress(), + this.getNote(), this.getTags(), this.product, this.price, this.getRating()); + supplierToReturn.toPin(); + return supplierToReturn; + } + //@@author + + //@@author yleeyilin + /** + * Returns a new instantiation of the current {@code Supplier} with the updated unpin, + * which throws {@code UnsupportedOperationException} if modification is attempted. + */ + @Override + public Supplier updateToUnpinned() { + Supplier supplierToReturn = new Supplier(this.getName(), this.getPhone(), this.getEmail(), this.getAddress(), + this.getNote(), this.getTags(), this.product, this.price, this.getRating()); + supplierToReturn.toUnpin(); + return supplierToReturn; + } + //@@author + + //@@author chiageng + /** + * Returns true if both staffs have the same identity and data fields. + * This defines a stronger notion of equality between two supplier. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Supplier)) { + return false; + } + + Supplier otherPerson = (Supplier) other; + return super.equals(otherPerson) + && product.equals(otherPerson.product) + && price.equals(otherPerson.price); + } + + //@@author chiageng + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(super.hashCode(), product, price); + } + + //@@author chiageng + @Override + public String toString() { + return new ToStringBuilder(this) + .add("name", getName()) + .add("phone", getPhone()) + .add("email", getEmail()) + .add("address", getAddress()) + .add("tags", getTags()) + .add("rating", getRating()) + .add("product", product) + .add("price", price) + .toString(); + } +} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java index cc0a68d79f9..76b5898afdd 100644 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ b/src/main/java/seedu/address/model/person/UniquePersonList.java @@ -3,11 +3,13 @@ import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import java.util.Comparator; import java.util.Iterator; import java.util.List; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import seedu.address.logic.parser.Prefix; import seedu.address.model.person.exceptions.DuplicatePersonException; import seedu.address.model.person.exceptions.PersonNotFoundException; @@ -104,6 +106,222 @@ public ObservableList asUnmodifiableObservableList() { return internalUnmodifiableList; } + //@@author yleeyilin + /** + * Sorts the list based on the isPinned field of contacts. + * Tie-breaks using the lexographical order of the contact names. + */ + public void sortByPinnedStatus() { + internalList.sort(Comparator.comparing(Person::isPinned).reversed() + .thenComparing(p -> p.getName().toString())); + } + //@@author + + //@@author Joshy837 + /** + * Sorts the list based on the specified field. + * + * @param prefix The prefix representing the field to sort by. + */ + public void sortBy(Prefix prefix) { + String field = prefix.getPrefix(); + + switch (field.toLowerCase()) { + case "name": + sortByFieldName(); + break; + case "phone": + sortByFieldPhone(); + break; + case "email": + sortByFieldEmail(); + break; + case "address": + sortByFieldAddress(); + break; + case "tag": + sortByFieldTag(); + break; + case "salary": + sortByFieldSalary(); + break; + case "employment": + sortByFieldEmployment(); + break; + case "product": + sortByFieldProduct(); + break; + case "price": + sortByFieldPrice(); + break; + case "commission": + sortByFieldCommission(); + break; + case "skill": + sortByFieldSkill(); + break; + case "note": + sortByFieldNote(); + break; + case "pin": + sortByFieldPin(); + break; + case "rating": + sortByFieldRating(); + break; + default: + sortByFieldName(); // Default sorting by name + break; + } + + sortByFieldPin(); + } + + /** + * Sorts the list by name. + */ + private void sortByFieldName() { + internalList.sort(Comparator.comparing(p -> p.getName().toString())); + } + + /** + * Sorts the list by phone number. + */ + private void sortByFieldPhone() { + internalList.sort(Comparator.comparing(p -> Integer.parseInt(p.getPhone().toString()))); + } + + /** + * Sorts the list by email address. + */ + private void sortByFieldEmail() { + internalList.sort(Comparator.comparing(p -> p.getEmail().toString())); + } + + /** + * Sorts the list by address. + */ + private void sortByFieldAddress() { + internalList.sort(Comparator.comparing(p -> p.getAddress().toString())); + } + + /** + * Sorts the list by tag. + */ + private void sortByFieldTag() { + internalList.sort(Comparator.comparing(p -> p.getTags().toString())); + } + + /** + * Sorts the list by salary. + */ + private void sortByFieldSalary() { + internalList.sort(Comparator.comparing(p -> { + if (p instanceof Staff) { + Staff staff = (Staff) p; + String salary = staff.getSalary().toString(); + return "0" + salary.length() + salary; + } + return "1" + p.getName().toString(); + })); + } + + /** + * Sorts the list by employment status. + */ + private void sortByFieldEmployment() { + internalList.sort(Comparator.comparing(p -> { + if (p instanceof Staff) { + Staff staff = (Staff) p; + return "0" + staff.getEmployment().toString(); + } + return "1" + p.getName().toString(); + })); + } + + /** + * Sorts the list by product. + */ + private void sortByFieldProduct() { + internalList.sort(Comparator.comparing(p -> { + if (p instanceof Supplier) { + Supplier supplier = (Supplier) p; + String product = supplier.getProduct().toString(); + return "0" + product.length() + product; + } + return "1" + p.getName().toString(); + })); + } + + /** + * Sorts the list by price. + */ + private void sortByFieldPrice() { + internalList.sort(Comparator.comparing(p -> { + if (p instanceof Supplier) { + Supplier supplier = (Supplier) p; + return "0" + supplier.getPrice().toString(); + } + return "1" + p.getName().toString(); + })); + } + + /** + * Sorts the list by commission. + */ + private void sortByFieldCommission() { + internalList.sort(Comparator.comparing(p -> { + if (p instanceof Maintainer) { + Maintainer maintainer = (Maintainer) p; + String commission = maintainer.getCommission().toString(); + return "0" + commission.length() + commission; + } + return "1" + p.getName().toString(); + })); + } + + /** + * Sorts the list by skill. + */ + private void sortByFieldSkill() { + internalList.sort(Comparator.comparing(p -> { + if (p instanceof Maintainer) { + Maintainer maintainer = (Maintainer) p; + return "0" + maintainer.getSkill().toString(); + } + return "1" + p.getName().toString(); + })); + } + + /** + * Sorts the list by note. + */ + private void sortByFieldNote() { + internalList.sort(Comparator.comparing((Person p) -> { + String note = p.getNote().toString(); + if (note.equals("No note here")) { + return "1" + p.getName().toString(); + } else { + return "0" + p.getNote().toString(); + } + })); + } + + /** + * Sorts the list by PIN. + */ + private void sortByFieldPin() { + internalList.sort(Comparator.comparing((Person p) -> p.getPin().toString()).reversed()); + } + + /** + * Sorts the list by rating. + */ + private void sortByFieldRating() { + internalList.sort(Comparator.comparing((Person p) -> p.getRating().toString()).reversed()); + } + //@@author + @Override public Iterator iterator() { return internalList.iterator(); diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 1806da4facf..930e6eb59e8 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -6,11 +6,7 @@ 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.tag.Tag; /** @@ -19,6 +15,7 @@ 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")), @@ -37,6 +34,7 @@ public static Person[] getSamplePersons() { 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")) + */ }; } diff --git a/src/main/java/seedu/address/storage/JsonAdaptedMaintainer.java b/src/main/java/seedu/address/storage/JsonAdaptedMaintainer.java new file mode 100644 index 00000000000..799dc0ff288 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedMaintainer.java @@ -0,0 +1,21 @@ +package seedu.address.storage; + +import seedu.address.model.person.Maintainer; +import seedu.address.model.person.Person; + +//@@author chiageng +/** + * Jackson-friendly version of {@link Maintainer}. + */ +class JsonAdaptedMaintainer extends JsonAdaptedPerson { + /** + * Converts a given {@code Person} into this class for Jackson use. + */ + public JsonAdaptedMaintainer(Person source) { + super(source); + Maintainer supplier = (Maintainer) source; + + setSkill(supplier.getSkill().value); + setCommission(supplier.getCommission().value); + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java index bd1ca0f56c8..d55d6c27726 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java @@ -11,23 +11,43 @@ import seedu.address.commons.exceptions.IllegalValueException; import seedu.address.model.person.Address; +import seedu.address.model.person.Commission; import seedu.address.model.person.Email; +import seedu.address.model.person.Employment; +import seedu.address.model.person.Maintainer; import seedu.address.model.person.Name; +import seedu.address.model.person.Note; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Price; +import seedu.address.model.person.Product; +import seedu.address.model.person.Rating; +import seedu.address.model.person.Salary; +import seedu.address.model.person.Skill; +import seedu.address.model.person.Staff; +import seedu.address.model.person.Supplier; import seedu.address.model.tag.Tag; /** * Jackson-friendly version of {@link Person}. */ class JsonAdaptedPerson { - + //@@author chiageng public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; - private final String name; private final String phone; private final String email; private final String address; + private String employment; + private String salary; + private String product; + private String price; + private String skill; + private String commission; + //@@author + private String note; + private String rating; + private String pin; private final List tags = new ArrayList<>(); /** @@ -35,12 +55,29 @@ class JsonAdaptedPerson { */ @JsonCreator public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone, - @JsonProperty("email") String email, @JsonProperty("address") String address, - @JsonProperty("tags") List tags) { + @JsonProperty("email") String email, @JsonProperty("address") String address, + @JsonProperty("note") String note, @JsonProperty("rating") String rating, + @JsonProperty("tags") List tags, + @JsonProperty("salary") String salary, + @JsonProperty("employment") String employment, + @JsonProperty("product") String product, + @JsonProperty("price") String price, + @JsonProperty("skill") String skill, + @JsonProperty("pin") String pin, + @JsonProperty("commission") String commission) { this.name = name; this.phone = phone; this.email = email; this.address = address; + this.salary = salary; + this.employment = employment; + this.product = product; + this.price = price; + this.skill = skill; + this.note = note; + this.rating = rating; + this.pin = pin; + this.commission = commission; if (tags != null) { this.tags.addAll(tags); } @@ -54,11 +91,58 @@ public JsonAdaptedPerson(Person source) { phone = source.getPhone().value; email = source.getEmail().value; address = source.getAddress().value; + rating = source.getRating().toString(); + note = source.getNote().toString(); + pin = source.getPin().toString(); tags.addAll(source.getTags().stream() .map(JsonAdaptedTag::new) .collect(Collectors.toList())); } + //@@author chiageng + public void setEmployment(String employment) { + this.employment = employment; + } + //@@author + + //@@author chiageng + public void setSalary(String salary) { + this.salary = salary; + } + //@@author + + //@@author chiageng + public void setProduct(String product) { + this.product = product; + } + //@@author + + //@@author chiageng + public void setPrice(String price) { + this.price = price; + } + //@@author + + //@@author chiageng + public void setSkill(String skill) { + this.skill = skill; + } + //@@author + + //@@author chiageng + public void setCommission(String commission) { + this.commission = commission; + } + //@@author + + public void setPin(String pin) { + this.pin = pin; + } + + public void setRating(String rating) { + this.rating = rating; + } + /** * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. * @@ -70,6 +154,7 @@ public Person toModelType() throws IllegalValueException { personTags.add(tag.toModelType()); } + //@@author chiageng if (name == null) { throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); } @@ -102,8 +187,91 @@ public Person toModelType() throws IllegalValueException { } final Address modelAddress = new Address(address); + //@@author + + final Note modelNote = new Note(note); + final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); + + final Rating modelRating = new Rating(rating); + + //@@author chiageng + if (salary != null && employment != null) { + if (!Salary.isValidSalary(salary)) { + throw new IllegalValueException(Salary.MESSAGE_CONSTRAINTS); + } + if (!Employment.isValidEmployment(employment)) { + throw new IllegalValueException(Employment.MESSAGE_CONSTRAINTS); + } + final Salary modelSalary = new Salary(salary); + final Employment modelEmployment = new Employment(employment); + Staff currStaff = new Staff(modelName, modelPhone, modelEmail, modelAddress, modelNote, + modelTags, modelSalary, modelEmployment, modelRating); + currStaff.setNoteContent(note); + if (pin.equals("true")) { + currStaff.toPin(); + } + return currStaff; + } + + if (product != null && price != null) { + if (!Product.isValidProduct(product)) { + throw new IllegalValueException(Product.MESSAGE_CONSTRAINTS); + } + if (!Price.isValidPrice(price)) { + throw new IllegalValueException(Price.MESSAGE_CONSTRAINTS); + } + final Product modelProduct = new Product(product); + final Price modelPrice = new Price(price); + Supplier currSupplier = new Supplier(modelName, modelPhone, modelEmail, modelAddress, modelNote, modelTags, + modelProduct, modelPrice, modelRating); + currSupplier.setNoteContent(note); + if (pin.equals("true")) { + currSupplier.toPin(); + } + return currSupplier; + } + + if (skill != null && commission != null) { + if (!Skill.isValidSkill(skill)) { + throw new IllegalValueException(Skill.MESSAGE_CONSTRAINTS); + } + if (!Commission.isValidCommission(commission)) { + throw new IllegalValueException(Commission.MESSAGE_CONSTRAINTS); + } + final Skill modelSkill = new Skill(skill); + final Commission modelCommission = new Commission(commission); + Maintainer currMaintainer = new Maintainer(modelName, modelPhone, modelEmail, modelAddress, modelNote, + modelTags, modelSkill, modelCommission, modelRating); + currMaintainer.setNoteContent(note); + if (pin.equals("true")) { + currMaintainer.toPin(); + } + return currMaintainer; + } + //@@author + + if (note == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Note.class.getSimpleName())); + } + if (!Note.isValidNote(note)) { + throw new IllegalValueException(Note.MESSAGE_CONSTRAINTS); + } + + if (rating == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Rating.class.getSimpleName())); + } + if (!Rating.isValidRating(rating)) { + throw new IllegalValueException(Rating.MESSAGE_CONSTRAINTS); + } + + Person personToAdd = new Person(modelName, modelPhone, modelEmail, modelAddress, modelNote, modelTags, + modelRating); + + if (pin.equals("true")) { + personToAdd.toPin(); + } + return personToAdd; } } diff --git a/src/main/java/seedu/address/storage/JsonAdaptedStaff.java b/src/main/java/seedu/address/storage/JsonAdaptedStaff.java new file mode 100644 index 00000000000..9dda5a66140 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedStaff.java @@ -0,0 +1,22 @@ +package seedu.address.storage; + +import seedu.address.model.person.Person; +import seedu.address.model.person.Staff; + +//@@author chiageng +/** + * Jackson-friendly version of {@link Staff}. + */ +class JsonAdaptedStaff extends JsonAdaptedPerson { + /** + * Converts a given {@code Person} into this class for Jackson use. + */ + public JsonAdaptedStaff(Person source) { + super(source); + Staff staff = (Staff) source; + + setSalary(staff.getSalary().value); + setEmployment(staff.getEmployment().value); + + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedSupplier.java b/src/main/java/seedu/address/storage/JsonAdaptedSupplier.java new file mode 100644 index 00000000000..0d5c05801fe --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedSupplier.java @@ -0,0 +1,21 @@ +package seedu.address.storage; + +import seedu.address.model.person.Person; +import seedu.address.model.person.Supplier; + +//@@author chiageng +/** + * Jackson-friendly version of {@link Supplier}. + */ +class JsonAdaptedSupplier extends JsonAdaptedPerson { + /** + * Converts a given {@code Person} into this class for Jackson use. + */ + public JsonAdaptedSupplier(Person source) { + super(source); + Supplier supplier = (Supplier) source; + + setProduct(supplier.getProduct().value); + setPrice(supplier.getPrice().value); + } +} diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java index 5efd834091d..9a1eeb2cb28 100644 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java @@ -11,7 +11,10 @@ import seedu.address.commons.exceptions.IllegalValueException; import seedu.address.model.AddressBook; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.person.Maintainer; import seedu.address.model.person.Person; +import seedu.address.model.person.Staff; +import seedu.address.model.person.Supplier; /** * An Immutable AddressBook that is serializable to JSON format. @@ -37,7 +40,20 @@ public JsonSerializableAddressBook(@JsonProperty("persons") List { + if (person instanceof Staff) { + return new JsonAdaptedStaff(person); + } else if (person instanceof Maintainer) { + return new JsonAdaptedMaintainer(person); + } else if (person instanceof Supplier) { + return new JsonAdaptedSupplier(person); + } else { + return new JsonAdaptedPerson(person); + } + }) + .collect(Collectors.toList())); } /** @@ -49,6 +65,7 @@ public AddressBook toModelType() throws IllegalValueException { AddressBook addressBook = new AddressBook(); for (JsonAdaptedPerson jsonAdaptedPerson : persons) { Person person = jsonAdaptedPerson.toModelType(); + if (addressBook.hasPerson(person)) { throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON); } diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java index 3f16b2fcf26..2bfb2009089 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/address/ui/HelpWindow.java @@ -11,11 +11,11 @@ import seedu.address.commons.core.LogsCenter; /** - * Controller for a help page + * Controller for all help pages for different commands. */ 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://ay2324s2-cs2103t-w10-2.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); @@ -28,7 +28,7 @@ public class HelpWindow extends UiPart { private Label helpMessage; /** - * Creates a new HelpWindow. + * Creates a new HelpWindow with a set default message. * * @param root Stage to use as the root of the HelpWindow. */ @@ -63,9 +63,11 @@ public HelpWindow() { * */ public void show() { - logger.fine("Showing help page about the application."); + String loggerSuccessMsg = "Showing help page about the application."; + logger.fine(loggerSuccessMsg); getRoot().show(); getRoot().centerOnScreen(); + getRoot().sizeToScene(); } /** @@ -83,10 +85,20 @@ public void hide() { } /** - * Focuses on the help window. + * Focuses on the help window and updates the size of help window. */ public void focus() { getRoot().requestFocus(); + getRoot().sizeToScene(); + } + + /** + * Sets the help message based on the command to give help for. + * + * @param message Help message based on command to give help for. + */ + public void setHelpMessage(String message) { + helpMessage.setText(message); } /** diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 79e74ef37c0..f3a310d702b 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -15,6 +15,7 @@ import seedu.address.logic.Logic; import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.messages.HelpMessages; import seedu.address.logic.parser.exceptions.ParseException; /** @@ -25,6 +26,8 @@ public class MainWindow extends UiPart { private static final String FXML = "MainWindow.fxml"; + private static final String TITLE = "Pooch Planner"; + private final Logger logger = LogsCenter.getLogger(getClass()); private Stage primaryStage; @@ -33,8 +36,12 @@ public class MainWindow extends UiPart { // Independent Ui parts residing in this Ui container private PersonListPanel personListPanel; private ResultDisplay resultDisplay; + private HelpWindow helpWindow; + @FXML + private StackPane titlePlaceholder; + @FXML private StackPane commandBoxPlaceholder; @@ -137,9 +144,99 @@ private void setWindowDefaultSize(GuiSettings guiSettings) { /** * Opens the help window or focuses on it if it's already opened. + * + * @param commandResult Message received from execute method which indicates + * the type of command to give help for. + */ + @FXML + public void handleAllHelp(CommandResult commandResult) { + String userFeedback = commandResult.getFeedbackToUser(); + if (!commandResult.isShowHelp()) { + return; + } + + // to determine which command user needs help for. + Boolean isHelpGeneralCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_HELP_MESSAGE); + Boolean isDeleteHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_DELETE_HELP_MESSAGE); + Boolean isEditHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_EDIT_HELP_MESSAGE); + Boolean isSearchHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_SEARCH_HELP_MESSAGE); + Boolean isAddHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_ADD_HELP_MESSAGE); + Boolean isExitHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_EXIT_HELP_MESSAGE); + Boolean isListHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_LIST_HELP_MESSAGE); + Boolean isNoteHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_NOTE_HELP_MESSAGE); + Boolean isPinHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_PIN_HELP_MESSAGE); + Boolean isUnpinHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_UNPIN_HELP_MESSAGE); + Boolean isRateHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_RATE_HELP_MESSAGE); + Boolean isRedoHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_REDO_HELP_MESSAGE); + Boolean isUndoHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_UNDO_HELP_MESSAGE); + Boolean isRemindHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_REMIND_HELP_MESSAGE); + Boolean isSortHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_SORT_HELP_MESSAGE); + Boolean isClearHelpCommand = userFeedback.equals(HelpMessages.MESSAGES_SHOWING_CLEAR_HELP_MESSAGE); + + // set different help messages based on type of command to get help for. + if (isHelpGeneralCommand) { + handleHelp(); + } else if (isDeleteHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_DELETE_MESSAGE); + } else if (isEditHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_EDIT_MESSAGE); + } else if (isSearchHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_SEARCH_MESSAGE); + } else if (isAddHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_ADD_MESSAGE); + } else if (isExitHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_EXIT_MESSAGE); + } else if (isListHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_LIST_MESSAGE); + } else if (isNoteHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_NOTE_MESSAGE); + } else if (isPinHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_PIN_MESSAGE); + } else if (isUnpinHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_UNPIN_MESSAGE); + } else if (isRateHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_RATE_MESSAGE); + } else if (isRedoHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_REDO_MESSAGE); + } else if (isUndoHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_UNDO_MESSAGE); + } else if (isRemindHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_REMIND_MESSAGE); + } else if (isSortHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_SORT_MESSAGE); + } else if (isClearHelpCommand) { + handleOtherHelp(HelpMessages.DISPLAYED_CLEAR_MESSAGE); + } else { + // As particular command cannot be identified, display general help message + // to still provide user with some help. + handleHelp(); + } + } + + /** + * Opens the help window for a specific command or focuses on it if it's already opened. + * + * @param displayedMessage Message to be displayed to users. + */ + @FXML + public void handleOtherHelp(String displayedMessage) { + helpWindow.setHelpMessage(displayedMessage); + if (!helpWindow.isShowing()) { + helpWindow.show(); + } else { + helpWindow.focus(); + } + } + + + /** + * Opens the general help window or focuses on it if it's already opened. */ @FXML public void handleHelp() { + String generalHelpMessage = + "Refer to the user guide: https://ay2324s2-cs2103t-w10-2.github.io/tp/UserGuide.html"; + helpWindow.setHelpMessage(generalHelpMessage); if (!helpWindow.isShowing()) { helpWindow.show(); } else { @@ -179,13 +276,11 @@ private CommandResult executeCommand(String commandText) throws CommandException resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()); if (commandResult.isShowHelp()) { - handleHelp(); + handleAllHelp(commandResult); } - if (commandResult.isExit()) { handleExit(); } - return commandResult; } catch (CommandException | ParseException e) { logger.info("An error occurred while executing command: " + commandText); diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java index 094c42cda82..5d38307364f 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/seedu/address/ui/PersonCard.java @@ -7,7 +7,10 @@ import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; +import seedu.address.model.person.Maintainer; import seedu.address.model.person.Person; +import seedu.address.model.person.Staff; +import seedu.address.model.person.Supplier; /** * An UI component that displays information of a {@code Person}. @@ -39,7 +42,27 @@ public class PersonCard extends UiPart { @FXML private Label email; @FXML + private Label salary; + @FXML + private Label employment; + @FXML + private Label product; + @FXML + private Label price; + @FXML + private Label skill; + @FXML + private Label commission; + @FXML + private Label className; + @FXML private FlowPane tags; + @FXML + private Label note; + @FXML + private Label rating; + @FXML + private Label pin; /** * Creates a {@code PersonCode} with the given {@code Person} and index to display. @@ -51,9 +74,66 @@ public PersonCard(Person person, int displayedIndex) { name.setText(person.getName().fullName); phone.setText(person.getPhone().value); address.setText(person.getAddress().value); + String currentRating = person.getRating().toString(); + if ("0".equals(currentRating)) { + rating.setText(""); + rating.setManaged(false); + } else { + int intValue = Integer.parseInt(currentRating); + rating.setText("⭐".repeat(Math.max(0, intValue))); + rating.setManaged(true); + } + if (person.getPin().getIsPinned()) { + pin.setText("📌 "); + pin.setManaged(true); + } + + String currentNote = person.getNote().toString(); + if (!"No note here".equals(currentNote)) { + note.setText(currentNote); + note.setManaged(true); + // Set the preferred width of the note + note.setPrefWidth(currentNote.length() * 10); + note.setWrapText(true); // Allow text wrapping + } else { + note.setManaged(false); + } email.setText(person.getEmail().value); person.getTags().stream() .sorted(Comparator.comparing(tag -> tag.tagName)) .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + if (person instanceof Staff) { + Staff staff = (Staff) person; + salary.setText(staff.getSalary().value); + employment.setText(staff.getEmployment().value); + salary.setManaged(true); + employment.setManaged(true); + salary.setVisible(true); + employment.setVisible(true); + } else if (person instanceof Supplier) { + Supplier supplier = (Supplier) person; + product.setText(supplier.getProduct().value); + price.setText(supplier.getPrice().value); + product.setManaged(true); + price.setManaged(true); + product.setVisible(true); + price.setVisible(true); + } else if (person instanceof Maintainer) { + Maintainer maintainer = (Maintainer) person; + skill.setText(maintainer.getSkill().value); + commission.setText(maintainer.getCommission().value); + skill.setManaged(true); + commission.setManaged(true); + skill.setVisible(true); + commission.setVisible(true); + } + } + + public Label getSalary() { + return salary; + } + + public Label getEmployment() { + return employment; } } diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java index f4c501a897b..20171b166bc 100644 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ b/src/main/java/seedu/address/ui/PersonListPanel.java @@ -1,13 +1,10 @@ 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; /** @@ -15,7 +12,6 @@ */ public class PersonListPanel extends UiPart { private static final String FXML = "PersonListPanel.fxml"; - private final Logger logger = LogsCenter.getLogger(PersonListPanel.class); @FXML private ListView personListView; diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/address/ui/ResultDisplay.java index 7d98e84eedf..a3a065e8d70 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/seedu/address/ui/ResultDisplay.java @@ -16,6 +16,11 @@ public class ResultDisplay extends UiPart { @FXML private TextArea resultDisplay; + /** + * Constructs a new ResultDisplay object. + * This constructor initializes the ResultDisplay with the FXML file specified, + * and sets the text of the resultDisplay to the provided OUTPUT_MESSAGE. + */ public ResultDisplay() { super(FXML); } diff --git a/src/main/resources/images/pooch.png b/src/main/resources/images/pooch.png new file mode 100644 index 00000000000..44019f9f80f Binary files /dev/null and b/src/main/resources/images/pooch.png differ diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 36e6b001cd8..d3d25dea85a 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -1,3 +1,5 @@ +/* @@author Joshy837 */ + .background { -fx-background-color: derive(#1d1d1d, 20%); background-color: #383838; /* Used in the default.html file */ @@ -5,32 +7,34 @@ .label { -fx-font-size: 11pt; - -fx-font-family: "Segoe UI Semibold"; + -fx-font-family: "Roboto"; -fx-text-fill: #555555; -fx-opacity: 0.9; } .label-bright { -fx-font-size: 11pt; - -fx-font-family: "Segoe UI Semibold"; + -fx-font-family: "Roboto"; -fx-text-fill: white; -fx-opacity: 1; } .label-header { -fx-font-size: 32pt; - -fx-font-family: "Segoe UI Light"; + -fx-font-family: "Roboto"; -fx-text-fill: white; -fx-opacity: 1; } .text-field { -fx-font-size: 12pt; - -fx-font-family: "Segoe UI Semibold"; + -fx-font-family: "Roboto"; + -fx-background-radius: 20px; } .tab-pane { -fx-padding: 0 0 0 1; + -fx-background-radius: 20px; } .tab-pane .tab-header-area { @@ -41,11 +45,12 @@ .table-view { -fx-base: #1d1d1d; - -fx-control-inner-background: #1d1d1d; + -fx-control-inner-background: transparent; -fx-background-color: #1d1d1d; -fx-table-cell-border-color: transparent; -fx-table-header-border-color: transparent; -fx-padding: 5; + -fx-background-radius: 20px; } .table-view .column-header-background { @@ -66,7 +71,7 @@ .table-view .column-header .label { -fx-font-size: 20pt; - -fx-font-family: "Segoe UI Light"; + -fx-font-family: "Roboto"; -fx-text-fill: white; -fx-alignment: center-left; -fx-opacity: 1; @@ -77,42 +82,50 @@ } .split-pane:horizontal .split-pane-divider { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: transparent; -fx-border-color: transparent transparent transparent #4d4d4d; } .split-pane { -fx-border-radius: 1; -fx-border-width: 1; - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: transparent; } .list-view { -fx-background-insets: 0; -fx-padding: 0; - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: transparent; + -fx-background-radius: 0; } .list-cell { - -fx-label-padding: 0 0 0 0; - -fx-graphic-text-gap : 0; - -fx-padding: 0 0 0 0; + -fx-label-padding: 0 0 10 0; /* Increase padding between each contact */ + -fx-graphic-text-gap: 0; + -fx-padding: 0 0 10 0; /* Increase padding between each contact */ + -fx-background-insets: 0 0 10px 0; /* Increase space below each contact */ + -fx-background-radius: 20px; + -fx-background-color: transparent; + -fx-margin: 0 0 20px 0; /* Increase margin below each contact */ } .list-cell:filled:even { - -fx-background-color: #3c3e3f; + -fx-background-color: rgba(60, 62, 63, 0.6); + -fx-background-radius: 20px; } .list-cell:filled:odd { - -fx-background-color: #515658; + -fx-background-color: rgba(81, 86, 88, 0.6); + -fx-background-radius: 20px; } .list-cell:filled:selected { -fx-background-color: #424d5f; + -fx-background-radius: 20px; } .list-cell:filled:selected #cardPane { - -fx-border-color: #3e7b91; + -fx-border-color: transparent; -fx-border-width: 1; } @@ -121,25 +134,24 @@ } .cell_big_label { - -fx-font-family: "Segoe UI Semibold"; - -fx-font-size: 16px; + -fx-font-family: "Roboto"; + -fx-font-size: 17px; + -fx-font-weight: bold; -fx-text-fill: #010504; } .cell_small_label { - -fx-font-family: "Segoe UI"; + -fx-font-family: "Roboto"; -fx-font-size: 13px; -fx-text-fill: #010504; } .stack-pane { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: transparent; } .pane-with-border { - -fx-background-color: derive(#1d1d1d, 20%); - -fx-border-color: derive(#1d1d1d, 10%); - -fx-border-top-width: 1px; + -fx-background-color: rgba(92, 64, 51, 0.6); } .status-bar { @@ -148,7 +160,7 @@ .result-display { -fx-background-color: transparent; - -fx-font-family: "Segoe UI Light"; + -fx-font-family: "Roboto"; -fx-font-size: 13pt; -fx-text-fill: white; } @@ -158,7 +170,7 @@ } .status-bar .label { - -fx-font-family: "Segoe UI Light"; + -fx-font-family: "Roboto"; -fx-text-fill: white; -fx-padding: 4px; -fx-pref-height: 30px; @@ -175,17 +187,15 @@ } .grid-pane { - -fx-background-color: derive(#1d1d1d, 30%); - -fx-border-color: derive(#1d1d1d, 30%); - -fx-border-width: 1px; + -fx-background-color: rgba(92, 64, 51, 0.6); } .grid-pane .stack-pane { - -fx-background-color: derive(#1d1d1d, 30%); + -fx-background-color: transparent; } .context-menu { - -fx-background-color: derive(#1d1d1d, 50%); + -fx-background-color: rgba(92, 64, 51, 0.6); } .context-menu .label { @@ -193,12 +203,12 @@ } .menu-bar { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: rgba(92, 64, 51, 0.6); } .menu-bar .label { -fx-font-size: 14pt; - -fx-font-family: "Segoe UI Light"; + -fx-font-family: "Roboto"; -fx-text-fill: white; -fx-opacity: 0.9; } @@ -216,9 +226,9 @@ -fx-padding: 5 22 5 22; -fx-border-color: #e2e2e2; -fx-border-width: 2; - -fx-background-radius: 0; + -fx-background-radius: 20px; -fx-background-color: #1d1d1d; - -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif; + -fx-font-family: "Roboto", Helvetica, Arial, sans-serif; -fx-font-size: 11pt; -fx-text-fill: #d8d8d8; -fx-background-insets: 0 0 0 0, 0, 1, 2; @@ -282,11 +292,11 @@ } .scroll-bar { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: transparent; } .scroll-bar .thumb { - -fx-background-color: derive(#1d1d1d, 50%); + -fx-background-color: rgba(92, 64, 51, 0.6); -fx-background-insets: 3; } @@ -318,12 +328,12 @@ } #commandTextField { - -fx-background-color: transparent #383838 transparent #383838; + -fx-background-color: rgba(92, 64, 51, 0.6); -fx-background-insets: 0; - -fx-border-color: #383838 #383838 #ffffff #383838; - -fx-border-insets: 0; + -fx-border-color: transparent; + -fx-border-insets: 2; -fx-border-width: 1; - -fx-font-family: "Segoe UI Light"; + -fx-font-family: "Roboto"; -fx-font-size: 13pt; -fx-text-fill: white; } @@ -332,8 +342,13 @@ -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); } +#resultDisplay { + -fx-border-color: transparent; + -fx-background-color: transparent; +} + #resultDisplay .content { - -fx-background-color: transparent, #383838, transparent, #383838; + -fx-background-color: rgba(92, 64, 51, 0.84); -fx-background-radius: 0; } @@ -346,7 +361,18 @@ -fx-text-fill: white; -fx-background-color: #3e7b91; -fx-padding: 1 3 1 3; - -fx-border-radius: 2; - -fx-background-radius: 2; - -fx-font-size: 11; + -fx-border-radius: 5; + -fx-background-radius: 5; + -fx-font-size: 13; +} + +.title { + -fx-background-color: rgba(92, 64, 51, 0.6); +} + +.title-label { + -fx-font-family: 'Roboto'; + -fx-font-size: 28pt; + -fx-text-fill: white; + -fx-font-style: italic; } diff --git a/src/main/resources/view/HelpWindow.fxml b/src/main/resources/view/HelpWindow.fxml index e01f330de33..8e42236f7bc 100644 --- a/src/main/resources/view/HelpWindow.fxml +++ b/src/main/resources/view/HelpWindow.fxml @@ -21,7 +21,7 @@ -