diff --git a/README.md b/README.md index 13f5c77403f..7a5c1ec0191 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,29 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) +# ezFoodie :takeout_box: + +[![CI Status](https://github.com/AY2122S1-CS2103T-F12-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2122S1-CS2103T-F12-4/tp/actions) +[![codecov](https://codecov.io/gh/AY2122S1-CS2103T-F12-4/tp/branch/master/graph/badge.svg)](https://codecov.io/gh/AY2122S1-CS2103T-F12-4/tp) ![Ui](docs/images/Ui.png) -* This is **a sample project for Software Engineering (SE) students**.
- Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. - * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. - * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. -* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. +* Have you faced some issues when managing the members in your restaurant? `ezFoodie` is here! + +* `ezFoodie` is a desktop application that helps restaurants **keep track of their ever-growing list of members**. It is optimized for using via a **Command Line Interface (CLI)** while still having the benefits of a **Graphical User Interface (GUI)**. If you can type fast, ezFoodie can get your member management tasks done faster than traditional GUI applications. + +* If you are interested in using `ezFoodie`, head over to the [_Quick Start_ section of the **User Guide**](https://ay2122s1-cs2103t-f12-4.github.io/tp/UserGuide.html#quick-start). + +* If you are interested in developing `ezFoodie`, the [**Developer Guide**](https://ay2122s1-cs2103t-f12-4.github.io/tp/DeveloperGuide.html) is a good place to start. + +## Acknowledgements + +This project developed based on the **[Address Book Product Website](https://se-education.org/addressbook-level3)** project. Which is a part of the se-education.org initiative. + +The icons of the project were obtained from [ezfoodie_icon](https://www.brandcrowd.com/), [member_icon](https://www.percici.com/), [summary_icon](https://www.pngwing.com/) + +Libraries used: + +* [JavaFX](https://openjfx.io/) +* [Jackson](https://github.com/FasterXML/jackson) +* [JUnit5](https://github.com/junit-team/junit5) +* [PlantUML](https://plantuml.com/) + +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 information. diff --git a/build.gradle b/build.gradle index be2d2905dde..0b635ee19d4 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,10 @@ test { finalizedBy jacocoTestReport } +run { + enableAssertions = true +} + task coverage(type: JacocoReport) { sourceDirectories.from files(sourceSets.main.allSource.srcDirs) classDirectories.from files(sourceSets.main.output) @@ -66,7 +70,7 @@ dependencies { } shadowJar { - archiveName = 'addressbook.jar' + archiveName = 'ezfoodie.jar' } defaultTasks 'clean', 'test' diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml old mode 100644 new mode 100755 index 4c001417aea..17777fd8bdf --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -1,7 +1,7 @@ + "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" + "https://checkstyle.org/dtds/configuration_1_3.dtd"> - - - - + + + + + value="fall through|Fall through|fallthru|Fallthru|falls through|Falls through|fallthrough|Fallthrough|No break|NO break|no break|continue on"/> diff --git a/copyright.txt b/copyright.txt index 93aa2a39ce2..8bab5755e01 100644 --- a/copyright.txt +++ b/copyright.txt @@ -7,3 +7,12 @@ Copyright by Susumu Yoshida - http://www.mcdodesign.com/ Copyright by Jan Jan Kovařík - http://glyphicons.com/ - calendar.png - edit.png + +Copyright by BrandCrowd - https://www.brandcrowd.com/ +- ezfoodie_icon.png + +Copyright by Percici - https://www.percici.com/ +- member_icon.png + +Copyright by PNGWing - https://www.pngwing.com/ +- summary_icon.png diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..bb3a9abec9e 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -5,55 +5,68 @@ title: About Us We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg). -You can reach us at the email `seer[at]comp.nus.edu.sg` +You can reach us via email, as indicated below. ## Project team -### John Doe +--- + +### Hu Jiajun `e0556371@u.nus.edu` - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github@holmesjj](http://github.com/holmesjj)] +[[portfolio@holmesjj](team/holmesjj.md)] -* Role: Project Advisor +* Role: Tech lead, Git expert +* Responsibilities: Scheduling and tracking + +--- -### Jane Doe +### Raja Sudalaimuthu Mukund `mukundrs001@gmail.com` - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github@mukundrs](https://github.com/mukundrs)] +[[portfolio@mukundrs](team/mukundrs.md)] -* Role: Team Lead -* Responsibilities: UI +* Role: Developer +* Responsibilities: Documentation, deliverables and deadlines + +--- -### Johnny Doe +### Zhang Zhiyao `e0449925@u.nus.edu` - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github@zzybluebell](http://github.com/zzybluebell)] +[[portfolio@zzybluebell](team/zzybluebell.md)] * Role: Developer -* Responsibilities: Data +* Responsibilities: Integration, Testing and Documents -### Jean Doe +--- + +### Chen Shi Yao, Stephanie `stephaniechenshiyao@gmail.com` - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github@stephanie-csy](http://github.com/stephanie-csy)] +[[portfolio@stephanie-csy](team/stephanie-csy.md)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: Code quality + +--- -### James Doe +### Yang Yuzhao `e0425130@u.nus.edu` - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github@morrow1ndy](http://github.com/morrow1ndy)] +[[portfolio@morrow1ndy](team/morrow1ndy.md)] * Role: Developer -* Responsibilities: UI +* Responsibilities: Integration + +--- diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..b3c2335547f 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -7,9 +7,28 @@ title: Developer Guide -------------------------------------------------------------------------------------------------------------------- +## **Introduction** + +This document is the developer guide for ezFoodie, an member manamgement application for restaurant managers and staffs. + +This developer guide serves to provide developers with an understanding on how ezFoodie is designed and developed. + +-------------------------------------------------------------------------------------------------------------------- + ## **Acknowledgements** -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +This project developed based on the **[Address Book Product Website](https://se-education.org/addressbook-level3)** project. Which is a part of the se-education.org initiative. + +The icons of the project were obtained from [ezfoodie_icon](https://www.brandcrowd.com/), [member_icon](https://www.percici.com/), [summary_icon](https://www.pngwing.com/) + +Libraries used: + +* [JavaFX](https://openjfx.io/) +* [Jackson](https://github.com/FasterXML/jackson) +* [JUnit5](https://github.com/junit-team/junit5) +* [PlantUML](https://plantuml.com/) + +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 information. -------------------------------------------------------------------------------------------------------------------- @@ -23,12 +42,12 @@ Refer to the guide [_Setting up and getting started_](SettingUp.md).
-:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/se-edu/addressbook-level3/tree/master/docs/diagrams/) folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams. +:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/AY2122S1-CS2103T-F12-4/tp/tree/master/docs/diagrams/) folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
### Architecture - + The ***Architecture Diagram*** given above explains the high-level design of the App. @@ -36,7 +55,7 @@ Given below is a quick overview of main components and how they interact with ea **Main components of the architecture** -**`Main`** has two classes called [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java). It is responsible for, +**`Main`** has two classes called [`Main`](https://github.com/AY2122S1-CS2103T-F12-4/tp/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/AY2122S1-CS2103T-F12-4/tp/tree/master/src/main/java/seedu/address/MainApp.java). It is responsible for, * At app launch: Initializes the components in the correct sequence, and connects them up with each other. * At shut down: Shuts down the components and invokes cleanup methods where necessary. @@ -52,9 +71,9 @@ The rest of the App consists of four 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 components interact with each other for the scenario where the user issues the command `del -mem/ -i/1`. - +![Architecture Sequence Diagram](images/ArchitectureSequenceDiagram.png) Each of the four main components (also shown in the diagram above), @@ -69,84 +88,116 @@ 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/AY2122S1-CS2103T-F12-4/tp/tree/master/src/main/java/seedu/address/ui/Ui.java) + +![Structure of the UI Component MainWindow](images/UiClassDiagramMainWindow.png) + +Structure of the UI Component MainWindow + +Structure of the UI Component MemberListPanel + +Structure of the UI Component MemberListPanel + +Structure of the UI Component MemberViewWindow + +Structure of the UI Component MemberViewWindow + +Structure of the UI Component SummaryWindow -![Structure of the UI Component](images/UiClassDiagram.png) +Structure of the UI Component SummaryWindow -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. +Structure of the UI Component HelpWindow -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) +Structure of the UI Component HelpWindow + +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `MemberListPanel`, `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/AY2122S1-CS2103T-F12-4/tp/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/AY2122S1-CS2103T-F12-4/tp/tree/master/src/main/resources/view/MainWindow.fxml) The `UI` component, * executes user commands using the `Logic` component. * listens for changes to `Model` data so that the UI can be updated with the modified data. * keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands. -* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`. +* depends on some classes in the `Model` component, as it displays `Member` 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) +**API** : [`Logic.java`](https://github.com/AY2122S1-CS2103T-F12-4/tp/tree/master/src/main/java/seedu/address/logic/Logic.java) Here's a (partial) class diagram of the `Logic` component: - + How the `Logic` component works: -1. When `Logic` is called upon to execute a command, it uses the `AddressBookParser` class to parse the user command. -1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to add a person). +1. When `Logic` is called upon to execute a command, it uses the `EzFoodieParser` class to parse the user command. +1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddMemberCommand`) which is executed by the `LogicManager`. +1. The command can communicate with the `Model` when it is executed (e.g. to add a member). 1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. -The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call. +The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("del -mem/ -i/1")` API call. -![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) +![Interactions Inside the Logic Component for the `del -mem/ -i/1` Command](images/DeleteSequenceDiagram.png) -
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. +
:information_source: **Note:** The lifeline for `DeleteCommandPrefixParser`. `DeleteMemberCommandParser` and `DeleteMemberCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command: - - 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. +* `UVW` is a placeholder for the specific type of the command e.g., `add`, `edit` and `redeem` +* `XYZ` is a placeholder for the specific object of the command e.g., `member`, `transaction` and `reservation` +* All `UVWXYZCommandParser` classes with the same type (e.g., `UVWMemberCommandParser`, `UVWTransactionCommandParser`, ...) extend from the `UVWCommandParser` abstract class so that they can be treated similarly where possible e.g, during testing. +* All `UVWXYZCommandParser` classes (e.g., `AddMemberCommandParser`, `DeleteMemberCommandParser`, ...) 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) + + +* Case1: `add`, `edit`, `redeem` and `delete` commands + * When called upon to parse a user command, the `EzFoodieParser` class creates a `UVWCommandPrefixParser` (e.g., `AddCommandPrefixParser`) which uses the other classes shown above to parse the user command by the specific object of the command (e.g., `member`, `transaction` and `reservation` for `AddCommandParser` correspond to `AddMemberCommandParser`, `AddTransactionCommandParser` and `AddReservationCommandParser` respectively) and create a `UVWXYZCommandParser` which extends from `UVWXCommandParser` (e.g., `AddMmeberCommandParser` extends from `AddCommandParser`). + * Then the `UVWXYZCommandParser` (e.g., `AddMmeberCommandParser`) uses the other classes shown above to parse the user command further and create a `UVWXYZCommand` object (e.g., `AddMmeberCommand`) which the `EzFoodieParser` returns back as a `Command` object. + + + +* Case2: `find`, `show`, `list`, `sort`, `login` and `setAccount` commands + * When called upon to parse a user command, the `EzFoodieParser` class creates a `UVWCommandParser` (e.g., `FindCommandParser`) which uses the other classes shown above to parse the user command and create a `UVWCommand` object (e.g., `FindCommand`) which the `EzFoodieParser` returns back as a `Command` object. + +
- +* Case3: `clear`, `exit`, `logout`, `help` and `summary` commands + * When called upon to parse a user command, the `EzFoodieParser` class creates a `UVWCommand` object directly (e.g., `ClearCommand`) which the `EzFoodieParser` returns back as a `Command` object. +### Model component +**API** : [`Model.java`](https://github.com/AY2122S1-CS2103T-F12-4/tp/tree/master/src/main/java/seedu/address/model/Model.java) + +![Model Class Diagram](images/ModelClassDiagram.png) The `Model` component, -* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object). -* stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. +* stores the ezFoodie data i.e., all `Member` objects (which are contained in a `UniqueMemberList` object). +* stores the currently 'selected' `Member` 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.
+
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Reservation` list in the `EzFoodie`, which `Member` references. This allows `EzFoodie` to only require one `Reservation` object per unique reservation, instead of each `Member` needing their own `Reservation` objects.
- +![Better ModelClass Diagram](images/BetterModelClassDiagram.png)
- ### Storage component -**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) +**API** : [`Storage.java`](https://github.com/AY2122S1-CS2103T-F12-4/tp/tree/master/src/main/java/seedu/address/storage/Storage.java) - + 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). +* can save both ezFoodie data and user preference data in json format, and read them back into corresponding objects. +* inherits from `AccountStorage`, `EzFoodieStorage` 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`) ### Common classes -Classes used by multiple components are in the `seedu.addressbook.commons` package. +Classes used by multiple components are in the `seedu.address.commons` package. -------------------------------------------------------------------------------------------------------------------- @@ -154,42 +205,473 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa This section describes some noteworthy details on how certain features are implemented. +### Find feature + +`[written by: Hu Jiajun]` + +#### Implementation + +Given below is an example usage scenario and how the find mechanism behaves at each step. + +1. The user executes `find -mem/ -id/00001` command to find the member with the member id `00001` in the application. + +2. The command is handled by `LogicManager#execute(String)`, which then calls and passes this command to the `EzFoodieParser#parseCommand(String)` method. + +3. The `EzFoodieParser` detects the command word `find` in the string and extracts the argument string `-mem/ -id/00001`. + +4. The `EzFoodieParser` creates a new `FindCommandParser` instance to parse the argument string according to the format specified for `FindCommand`. + +5. The argument string is parsed to the member ids array `[00001]` using the `FindCommandParser#parse(String)` method, which also performs validation. + +6. The `FindCommandParser` creates a new `IdContainsKeywordsPredicate` instance with the member ids array `[00001]` to handle the filter. + +7. The `FindCommandParser` creates a new `FindCommand` instance with the `IdContainsKeywordsPredicate` instance and returns it to `EzFoodieParser`, which in turn returns it to `LogicManager`. + +8. The `LogicManager` calls the `FindCommand#execute(Model)` method. + +9. The `FindCommand` calls the `Model#updateFilteredMemberList(IdContainsKeywordsPredicate)` method. + +10. The `Model` calls the `FilteredList#setPredicate(IdContainsKeywordsPredicate)` to filter the member by the member id `00001` + +11. The application lists the filtered member. + +12. Lastly, the `FindCommand` creates a `CommandResult` with a `SuccessMessage` and returns it to `LogicManager`. + +The above process is shown in the following sequence diagram: + +![FindSequenceDiagram](images/FindSequenceDiagram.png) + +**Sequence diagram showcasing the find command process** + +
:information_source: **Note:** The lifeline for `FindCommandParser`, `IdContainsKeywordsPredicate` and `FindCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. + +
+ +The following activity diagram summarizes what happens when a user executes a new command to find the members by keywords: + + + +**Activity diagram showcasing the find command execution flow** + +#### Design consideration + +**Aspect: How to execute different types of keywords with `FindCommand`** + +* **Alternative 1 (current choice):** Add corresponding constructors for different types of keywords. + * Pros: Easy to implement, only need to make a few changes to the source code. + * Cons: Insufficient use of object-oriented features (inheritance and polymorphic). + +* **Alternative 2:** Abstract `FindCommand`, create different classes according to different types of keywords to inherit `FindCommand`, such as `FindIdCommand`, `FindNameCommand`, `FindEmailCommand`, etc. + * Pros: Sufficient use of object-oriented features (inheritance and polymorphic). + * Cons: Have to make more changes to the source code, it may cause potential bugs. + +### Sort feature + +`[written by: Hu Jiajun]` + +#### Implementation + +Given below is an example usage scenario and how the sort mechanism behaves at each step. + +1. The user executes `sort -mem/ -c/ -a/` command to sort the members by their credits in ascending order in the application. + +2. The command is handled by `LogicManager#execute(String)`, which then calls and passes this command to the `EzFoodieParser#parseCommand(String)` method. + +3. The `EzFoodieParser` detects the command word `sort` in the string and extracts the argument string `-mem/ -c/ -a/`. + +4. The `EzFoodieParser` creates a new `SortCommandParser` instance to parse the argument string according to the format specified for `SortCommand`. + +5. The argument string is parsed to `a` (ascending order) and converted to the enum tpye `SortStatus.ASC` using the `SortCommandParser#parse(String)` method, which also performs validation. + +6. The `SortCommandParser` creates a new `CreditSortComparator` instance with the enum tpye `SortStatus.ASC` to handle the sort. + +7. The `SortCommandParser` creates a new `SortCommand` instance with the `CreditSortComparator` instance and returns it to `EzFoodieParser`, which in turn returns it to `LogicManager`. + +8. The `LogicManager` calls the `SortCommand#execute(Model)` method. + +9. The `SortCommand` calls the `Model#updateSortedMemberList(CreditSortComparator)` method. + +10. The `Model` calls the `SortedList#setComparator(CreditSortComparator)` method to sort the members by their credits in ascending order + +11. The application lists the sorted members. + +12. Lastly, the `SortCommand` creates a `CommandResult` with a `SuccessMessage` and returns it to `LogicManager`. + +The above process is shown in the following sequence diagram: + +![SortSequenceDiagram](images/SortSequenceDiagram.png) + +**Sequence diagram showcasing the sort command process** + +
:information_source: **Note:** The lifeline for `SortCommandParser`, `CreditSortComparator` and `SortCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. + +
+ +The following activity diagram summarizes what happens when a user executes a new command to sort the members by their credits: + + + +**Activity diagram showcasing the sort command execution flow** + +#### Design consideration + +**Aspect: How to sort by different types of fields with `SortCommand`** + +* **Alternative 1 (current choice):** Add corresponding constructors for different types of fields. + * Pros: Easy to implement, only need to make a few changes to the source code. + * Cons: Insufficient use of object-oriented features (inheritance and polymorphic). + +* **Alternative 2:** Abstract `SortCommand`, create different classes according to different types of fields to inherit `SortCommand`, such as `SortPointCommand`, `SortCreditCommand`, etc. + * Pros: Sufficient use of object-oriented features (inheritance and polymorphic). + * Cons: Have to make more changes to the source code, it may cause potential bugs. + + +### Add Transaction feature + +`[written by: Yang Yuzhao]` + +#### Implementation + +Given below is an example usage scenario and how adding transaction behaves at each step. + +1. The user executes `add -txn/ -b/23.00 -id/00001` command to add a transaction of billing amount `23.00` to the member with member ID `00001` in ezFoodie. + +2. The command is handled by `LogicManager#execute(String)`, + which then calls and passes this command to the `EzFoodieParser#parseCommand(String)` method. + +3. The `EzFoodieParser` detects the command word `add` in the string and extracts the argument string `-txn/ -b/23.00 -id/00001`. + +4. The `EzFoodieParser` creates a new `AddCommandPrefixParser` instance to parse the argument string + according to the format specified for `AddCommand`. + +5. The `AddCommandPrefixParser` detects the prefix word `-txn/` by the method `AddCommandPrefixParser#parse(arguments)` + called and creates a new `AddTransactionCommandParser` instance to parse the rest of the argument string. + +6. The `AddTransactionCommandParser`prefixes `-b/` and `-id/` + by the method `AddTransactionCommandParser#parser(arguments)` called + nd parse them into `billing` and `memberId` through `ParserUtil#parseBilling` and `ParserUtil#parseMemberId`. + +7. The `AddTransactionCommandParser#parser(arguments)` calls `ParserUtil#parseTimestamp` + to create a new `TimeStamp` instance `timestamp` and + `ParserUtil#parseTransactionId` to create a new `Id` instance `transactionId`, + and creates a new `Transaction` object `transaction` with `billing`, `timestamp` and `transactionId`. + +8. The `AddTransactionCommandParser#parser(arguments)` creates a new `AddTransactionCommand` instance + with `transaction` and `memberId` and returns it to `ezFoodieParser` which in turn returns it to `LogicManager`. + +9. The `LogicManager` calls the `AddTransactionCommand#execute(Model)` method. + +10. The `AddTransactionCommand` calls `Model#getUpdatedMemberList()` and + searches the list to find the member with the respective `memberID` to obtain `memberToEdit`. + +11. The `AddTransactionCommand` calls `AddTransactionCommand#createEditedMember(memberToedit, transaction)` + to obtain `editedMember`. + +12. The `AddTransactionCommand` calls `Model#setMember(memberToEdit, editedMember)` + to replace the old member with new transaction details. + +13. The `AddTransactionCommand` calls `Model#updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS)` to update the edited + member in the member list. + +14. The application lists the updated member list. + +15. Lastly, the `AddTransactionCommand` creates a new instance of `CommandResult` with a success message, + and returns it to `LogicManager`. + +The above process is shown in the following sequence diagram: + +![AddTransactionSequenceDiagram](images/AddTransactionSequenceDiagram.png) + +### Show Member Profile feature + +`[written by: Yang Yuzhao]` + +#### Implementation + +Given below is an example usage scenario and how showing member profile behaves at each step. + +1. The user executes `show -mem/ -id/00001` command to show the details of member with member ID `00001` in ezFoodie + in a separate window. + +2. The command is handled by `LogicManager#execute(String)`, + which then calls and passes this command to the `EzFoodieParser#parseCommand(String)` method. + +3. The `EzFoodieParser` detects the command word `show` in the string and extracts the argument string `-mem -id/00001`. + +4. The `EzFoodieParser` creates a new `ViewCommandParser` instance to parse the argument string + according to the format specified for `ViewCommand`. + +5. `ViewCommandParser` calls `ViewCommandParser#parse()` to parse the arguments into a new `Id` instance `id` + using `ParserUtil#parseMemberId(String)`. + +6. A new String array `idKeywords` is created by `value` attribute of `id`. + +7. `ViewCommandParser` creates a new `ViewCommand` instance with + a new `IdContainsKeywordsPredicate` created using `idKeywords` and + returns it to `ezFoodieParser` which in turn returns it to `LogicManager`. + +8. The `LogicManager` calls the `ViewCommand#execute(Model)` method. + +9. The `ViewCommand` calls `Model#updateFilteredMemberListForView(IdContainsKeywordsPredicate)` + to update the `filteredMembersForView` in `ModelManager` with filtered member to show. + +10. The `ViewCommand` creates a new instance of `CommandResult` with a success message + and a true value for `showMemberView`, and returns it to `LogicManager`. + +11. The application opens a separate `MemberViewWindow` with this member's profile. + + +### Summary feature + +`[written by: Yang Yuzhao]` + +#### Implementation + +Given below is an example usage scenario and how showing summary behaves at each step. + +1. The user executes `summary` command to show statistics about `Member` existing in ezFoodie + as well as time-series statistics of `Transaction` data. + +2. The command is handled by `LogicManager#execute(String)`, + which then calls and passes this command to the `EzFoodieParser#parseCommand(String)` method. + +3. The `EzFoodieParser` detects the command word `summary` in the string. + +4. `LoginStatus.getLoginStatus()` is called to check whether the user has logged in as a manager. + If not, a new `PermissionException` instance will be thrown with `MESSAGE_PERMISSION_DENIED`. + +5. If the user has logged in as a manager, `EzFoodieParser` creates a new `SummaryCommand` instance + and returns it to `ezFoodieParser` which in turn returns it to `LogicManager`. + +6. The `LogicManager` calls the `SummaryCommand#execute(Model)` method. + +7. The `ViewCommand` creates a new instance of `CommandResult` with a success message + and a true value for `showSummary`, and returns it to `LogicManager`. + +8. The application opens a separate `SummaryWindow` with statistics to show. + +The above process is shown in the following sequence diagram: + +![SummarySequenceDiagram](images/SummarySequenceDiagram.png) + + +### Add member feature + +`[written by: Zhang Zhiyao]` + +#### Implementation + +Given below is an example usage scenario and how the adding members behaves at each step. + +1. The user executes `add -mem/ -n/John Doe -p/98765432 -e/johndoe@gmail.com -a/112 Amoy Street, 069907, Singapore` command to add a member `John Doe` in the Ezfoodie and its' storage. + +2. The command is handled by `LogicManager#execute(String)`, which then calls and passes this command to the `EzFoodieParser#parseCommand(String)` method. + +3. The EzFoodieParser detects the command word `add` in the string and extracts the argument string + `-mem/ -n/John Doe -p/98765432 -e/johndoe@gmail.com -a/112 Amoy Street, 069907, Singapore`. + +4. The `EzFoodieParser` creates a new `AddCommandPrefixParser` instance to parse the argument string according to the format specified for `AddCommand` and calls `AddCommandPrefixParser#parse(arguments)`. + +5. `AddCommandPrefixParser#parse(arguments)` detects the prefix `-mem/` and creates a new instance of `AddMemberCommandParser` and calls `AddMemberCommandParser#parse(arguments)`. + +6. `AddMemberCommandParser#parse(arguments)` detects the prefixes `-n/`, `-p/`, `-e/` and `-a/` and parses them through `ParseUtil` to obtain the `name`, `phone`,`email` and `address`. + +7. Through the `ParseUtil`, it will get default `timestamp`, `credit`, `point`, `tagList`, `transactionList` and `reservationList`. + +7. Using the obtained `name`, `phone`,`email`, `address`, `timestamp`, `credit`,`point`, `tagList`, `transactionList` and `reservationList`. a new instance of `Member` is created. + +8. Using a new instance `Member`, a new instance of `AddMemberCommand` is created and returned to `ezFoodieParser` which in turn returns it to `LogicManager`. + +9. `LogicManager` calls the `AddMemberCommand#execute(Model)` method. + +10. Lastly, `AddMemberCommand` creates a new instance of `CommandResult` with a success message, and returns it to Logic Manager. + +#### Design consideration + +**What is the default value of `timestamp` ?** +* **default value** is the timestamp auto registerred by system at fist time add in the member. + +**What is the default value of `credit`,`point`, `tagList`, `transactionList` and `reservationList` ?** +* **default value** will all be set as 0 and empty list. + +### Update Credit feature + +`[written by: Zhang Zhiyao]` + +#### Implementation + +Given below is an example usage scenario and how the redeem mechanism behaves at each step. + +1. The user executes `add -txn/ -b/200.00 -id/00001` command to add transaction. + +2. The command is handled by `LogicManager#execute(String)`, which then calls and passes this command to the `EzFoodieParser#parseCommand(String)` method. + +3. The EzFoodieParser detects the command word `add` in the string and extracts the argument string `-txn/ -b/200.00 -id/00001`. + +4. The EzFoodieParser creates a new `AddCommandPrefixParser` instance to parse the argument string according to the format specified for `AddCommand` and calls `AddCommandPrefixParser#parse(arguments)`. + +5. `AddCommandPrefixParser#parse(arguments)` detects the prefix `-txn/` and creates a new instance of `AddTransactionCommandParser` and calls `AddTransactionCommandParser#parse(arguments)`. + +6. `AddTransactionCommandParser#parse(arguments)` detects the prefixes `-b/` and ` -id/` and parses them through `ParseUtil` to obtain the `billing amount` and the `memberID`. + +7. Using the obtained `billing amount`, a new instance of `Transaction` is created. According to the `billing amount`, the credit has been updated. + + +### Redeem point feature + +`[written by: Zhang Zhiyao]` + +#### Implementation + +Given below is an example usage scenario and how the redeem mechanism behaves at each step. + +1. when user redeem point, the user executes `redeem -rd/100 -id/00001` command。 + +2. The command is handled by `LogicManager#execute(String)`, which then calls and passes this command to the `EzFoodieParser#parseCommand(String)` method. + +3. The EzFoodieParser detects the command word `redeem` in the string and extracts the argument string `-rd/100 -id/00001`. + +4. The EzFoodieParser creates a new `RedeemCommandParser` instance to parse the argument string according to the format specified for `RedeemCommand`, and calls `RedeemCommandParser`. + +5. The `RedeemCommandParser` detects the prefixes `-b/`, `-id/` and parses them through `ParseUtil` to obtain the `billing amount`, the `memberID`. + +6. Using the obtained `billing amount`, the `RedeemCommand` will execute and update related point. then return a new `Member` with updated point. + + +### Add reservation +`[written by: Raja Sudalaimuthu Mukund]` + +#### Implementation + +Given below is an example usage scenario and how the add reservation mechanism behaves at each step. + +1. The user executes `add -rs/ -dt/2021-01-01 00:00 -rm/2 people -id/10001` command to add a reservation for 2 people at + 2021-01-01 00:00 hrs to member id 10001's reservation list. + +2. The command is handled by `LogicManager#execute(String)`, which then calls and passes this command to the `EzFoodieParser#parseCommand(String)` method. + +3. The EzFoodieParser detects the command word `add` in the string and extracts the argument string `-rs/ -dt/2021-01-01 00:00 -rm/2 people -id/10001`. + +4. The EzFoodieParser creates a new `AddCommandPrefixParser` instance to parse the argument string according to the format specified for `AddCommand` and calls `AddCommandPrefixParser#parse(arguments)`. + +5. `AddCommandPrefixParser#parse(arguments)` detects the prefix `-rs/` and creates a new instance of `AddReservationCommandParser` and calls `AddReservationCommandParser#parse(arguments)`. + +6. `AddReservationCommandParser#parse(arguments)` detects the prefixes `-dt/`, `-rm/` and `-id/` and parses them through `ParseUtil` to obtain the `dateTime`, `remark` and the `memberID`. + +7. Using the obtained `dateTime` and `remark`, a new instance of `Reservation` is created. + +8. Using the new instance of `Reservation` and the obtained `memberID`, a new instance of `AddReservationCommand` is created and returned to + ezFoodieParser which in turn returns it to `LogicManager`. + +9. `LogicManager` calls the `AddReservationCommand#execute(Model)` method. + +10. The `AddReservationCommand` calls `Model#getUpdatedMemberList()` and searches the list to find the member with the respective `memberID` to obtain `memberToEdit`. + +11. The `AddReservationCommand` calls `Model#createUpdatedReservations(memberToEdit, reservationToAdd)` to create a new + instance of the same member but with the new reservation added to the member's reservation list. + +12. The `AddReservationCommand` calls `Model#setMember(memberToEdit, editedMember)` to replace the old instance of the member + with its new instance. + +13. The `AddReservationCommand` calls `Model#updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS)` to update the current + member list with the updated member list. + +14. The application lists the updated member list. + +15. Lastly, `AddReservationCommand` creates a new instance of `CommandResult` with a success message, and returns it to Logic Manager. + +![AddReservationSequenceDiagram](images/AddReservationSequenceDiagram.png) + +#### Design consideration + +### Delete Reservation Feature + +`[written by: Chen Shi Yao, Stephanie]` + +#### Implementation + +Given below is an example usage scenario and how the delete reservation mechanism behaves at each step. + +1. The user executes `del -rs/ -id/10001100001` command to delete the reservation with reservationId 100001 for the + member with ID 10001. + +2. The command is handled by `LogicManager#execute(String)`, which then calls and passes this command to the + `EzFoodieParser#parseCommand(String)` method. + +3. The EzFoodieParser detects the command word `del` in the string and extracts the argument string + `-rs/ -id/10001100001`. + +4. The EzFoodieParser creates a new `DeleteCommandPrefixParser` instance to parse the argument string according to the + format specified for `DeleteCommand` and calls `DeleteCommandPrefixParser#parse(arguments)`. + +5. `DeleteCommandPrefixParser#parse(arguments)` detects the prefix `-rs/` and creates a new instance of + `DeleteReservationCommandParser` and calls `DeleteReservationCommandParser#parse(arguments)`. + +6. `DeleteReservationCommandParser#parse(arguments)` detects the prefixes `-id/` and parses them through + `ParseUtil` to obtain the `memberId` and `ReservationId`. + +7. Using the obtained `memberID` and `ReservationId`, a new instance of `DeleteReservationCommand` is created and returned to + ezFoodieParser which in turn returns it to `LogicManager`. + +8. `LogicManager` calls the `DeleteReservationCommand#execute(Model)` method. + +9. The `DeleteReservationCommand` calls `Model#getUpdatedMemberList()` and searches the list to find the member with the respective + `memberID` to obtain `memberToEdit`. + +10. Using the `reservationId`, the `DeleteReservationCommand` then searches the list of `Reservations` associated with the + `memberToEdit` to find the `reservationToDelete`. + +11. The `DeleteReservationCommand` calls `DeleteReservationCommand#createEditedMember(memberToEdit, reservationToDelete)` to create a new + instance of the same member but with the reservation deleted from the member's reservation list. + +12. The `DeleteReservationCommand` calls `Model#setMember(memberToEdit, editedMember)` to replace the old instance of the member + with its new instance. + +13. The `DeleteReservationCommand` calls `Model#updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS)` to update the current + member list with the updated member list. + +14. The application lists the updated member list. + +15. Lastly, `DeleteReservationCommand` creates a new instance of `CommandResult` with a success message, and returns it to Logic Manager. + +![DeleteReservationSequenceDiagram](images/DeleteReservationSequenceDiagram.png) + + ### \[Proposed\] Undo/redo feature #### Proposed Implementation -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 proposed undo/redo mechanism is facilitated by `VersionedEzFoodie`. It extends `EzFoodie` with an undo/redo history, stored internally as an `ezFoodieStateList` and `currentStatePointer`. Additionally, it implements 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. -* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history. +* `VersionedEzFoodie#commit()` — Saves the current ezFoodie state in its history. +* `VersionedEzFoodie#undo()` — Restores the previous ezFoodie state from its history. +* `VersionedEzFoodie#redo()` — Restores a previously undone ezFoodie state from its history. -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +These operations are exposed in the `Model` interface as `Model#commitEzFoodie()`, `Model#undoEzFoodie()` and `Model#redoEzFoodie()` respectively. 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. The `VersionedEzFoodie` will be initialized with the initial ezFoodie state, and the `currentStatePointer` pointing to that single ezFoodie 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 `del -mem/ -i/5` command to delete the 5th Member in the ezFoodie. The `delete` command calls `Model#commitEzFoodie()`, causing the modified state of the ezfoodie after the `del -mem/ -i/5` command executes to be saved in the `ezFoodieStateList`, and the `currentStatePointer` is shifted to the newly inserted ezFoodie 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 -mem/ -n/John Doe …​` to add a new member. The `add` command also calls `Model#commitEzFoodie()`, causing another modified ezFoodie state to be saved into the `ezFoodieStateList`. -![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 its execution, it will not call `Model#commitEzFoodie()`, so the ezFoodie state will not be saved into the `ezFoodieStateList`.
-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 the member was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoEzFoodie()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous ezFoodie state, and restores the ezFoodie 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` is at index 0, pointing to the initial EzFoodie state, then there are no previous EzFoodie states to restore. The `undo` command uses `Model#canUndoEzFoodie()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo.
@@ -201,35 +683,34 @@ The following sequence diagram shows how the undo operation works:
-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 `Model#redoEzFoodie()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the ezFoodie 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` is at index `ezFoodieStateList.size() - 1`, pointing to the latest ezFoodie state, then there are no undone EzFoodie states to restore. The `redo` command uses `Model#canRedoEzFoodie()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
-Step 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 command `list -mem/`. Commands that do not modify the ezFoodie, such as `list -mem/`, will usually not call `Model#commitEzFoodie()`, `Model#undoEzFoodie()` or `Model#redoEzFoodie()`. Thus, the `ezFoodieStateList` 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 `clear`, which calls `Model#commitEzFoodie()`. Since the `currentStatePointer` is not pointing at the end of the `ezFoodieStateList`, all ezFoodie states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add -mem/ -n/John Doe …​` command. This is the behavior that most modern desktop applications follow. -![UndoRedoState5](images/UndoRedoState5.png) + The following activity diagram summarizes what happens when a user executes a new command: -#### Design considerations: +#### Design considerations **Aspect: How undo & redo executes:** -* **Alternative 1 (current choice):** Saves the entire address book. +* **Alternative 1 (current choice):** Saves the entire ezFoodie. * 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). +* **Alternative 2:** Individual command knows how to undo/redo by itself. + * Pros: Will use less memory (e.g. for `delete`, just save the member being deleted). * Cons: We must ensure that the implementation of each individual command are correct. _{more aspects and alternatives to be added}_ @@ -251,127 +732,1081 @@ _{Explain here how the data archiving feature will be implemented}_ -------------------------------------------------------------------------------------------------------------------- -## **Appendix: Requirements** +## **Appendix 1: Requirements** ### Product scope -**Target user profile**: +**Target user profile story**: + +Ben is a restaurant manager. He found that restaurants are becoming more and more popular, the number of members is increasing, and there are a large number of reservations every day. Every day the front desk staffs spend a lot of time in excel or paper to record new members, record reservations, and find reservations. With the need to handle multiple tasks at the same time, the staffs are also prone to make careless mistakes at work due to fatigue. 😞 + +More importantly, the member list is only stored in excel, it makes it difficult for Ben to manage and analyze members to formulate targeted promotional strategies, which will have bad impact on the profit expansion of the restaurant. 😞 -* has a need to manage a significant number of contacts +As a manager who is proficient in technology and has commendable experience in Unix, Ben hopes to develop an easy-to-operate application to improve the work efficiency of himself and the staffs. 🤩 + +**Target user profile summary**: + +Managers and staffs who + +* work in a highly popular restaurant and the number of members is increasing +* are responsible for managing a large number of member registrations and reservations daily +* are required to multi-task (manage member registrations and reservations) +* need automatic reminder +* need to manage and analyze members to formulate promotional strategies +* are proficient in technology +* want to get things done quickly +* are tired of tracking member details from paper or excel * prefer desktop apps over other types * can type fast -* prefers typing to mouse interactions -* is reasonably comfortable using CLI apps +* prefer typing to mouse interactions +* are reasonably comfortable using CLI apps -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +**Value proposition**: +helps restaurants keep track of their ever-growing list of members. Restaurant managers and staffs can easily view and update member status (e.g. personal information, tier, reservation, transaction, etc.) to manage and analyze members based on various criteria faster than a typical mouse/GUI driven application. ### 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 | +| Priority | As a …​ | I want to …​ | So that I can …​ | +| -------- | --------| --------------------------------------------------------- | ---------------------------------------------------------------------- | +| `* * *` | staff | view help | easily check how to use the commands and access the user guide | +| `* * *` | staff | exit the program | | +| `* * *` | staff | list out all members | easily view and access the member basic information | +| `* * *` | staff | add new member | easily the track member statuses | +| `* * *` | staff | find members by name | easily find the specific members to check their details | +| `* * *` | staff | find member by phone | easily find the specific member to check his/her details | +| `* * *` | staff | find member by email | easily find the specific member to check his/her details | +| `* * *` | staff | find members by registration date | easily find a member to check their details | +| `* * *` | staff | find member by member ID | easily find the specific member to check his/her details | +| `* * *` | staff | show member profile | easily check the specific member's details | +| `* * *` | staff | add transaction by member ID | easily track the transaction history of members | +| `* * *` | staff | redeem member’s points by member ID | easily provide promotional offers for frequent customers | +| `* * *` | staff | redeem member’s points by index number | easily provide promotional offers for frequent customers | +| `* * *` | staff | add reservation by member ID | easily reserve seats for the comming customers | +| `* * *` | manager | clear the program | initialize the entire program | +| `* * *` | manager | login as a manager | access manager-only features, e.g. sort the members by their credits | +| `* * *` | manager | logout as a manager | prevent staff from accessing manager-only features | +| `* * *` | manager | set login password | improve the program security | +| `* * *` | manager | sort members by credit | easily analyze the members' consumption and distribution | +| `* * *` | manager | edit member name by member ID | update member information to latest | +| `* * *` | manager | edit member phone by member ID | update member information to latest | +| `* * *` | manager | edit member email by member ID | update member information to latest | +| `* * *` | manager | edit member address by member ID | update member information to latest | +| `* * *` | manager | edit member name by index number | update member information to latest | +| `* * *` | manager | edit member phone by index number | update member information to latest | +| `* * *` | manager | edit member email by index number | update member information to latest | +| `* * *` | manager | edit member address by index number | update member information to latest | +| `* * *` | manager | delete member by member ID | remove member that I no longer need | +| `* * *` | manager | delete member by index number | remove member that I no longer need | +| `* * ` | staff | edit reservation by member ID and transaction ID | update reservation for member to latest | +| `* * ` | manager | delete reservation by member ID and transaction ID | remove reservation for member that I no longer need | +| `* ` | staff | retrieve previous commands | easily speed up the typing speed of command | +| `* ` | manager | delete transaction by member ID and transaction ID | correct any accidental mistakes | +| `* ` | manager | edit transaction by member ID and transaction ID | correct any accidental mistakes | +| `* ` | manager | view summary for registrations and transactions | know the summary details in a certain period, e.g. total registrations | *{More to be added}* ### Use cases -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +(For all use cases below, the **System** is the `ezFoodie` and the **Actor** is the `Staff` or only the `Manager`, unless specified otherwise) + +**Use case: UC01 - View help** -**Use case: Delete a person** +**Actors: Staff** **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. Staff requests to view help. +2. ezFoodie shows a dialog contains how to use the commands and access the user guide. Use case ends. **Extensions** -* 2a. The list is empty. +* 1a. The given input is invalid. - Use case ends. + * 1a1. ezFoodie shows an error message. -* 3a. The given index is invalid. + Use case ends. - * 3a1. AddressBook shows an error message. +**Use case: UC02 - Exit the program** - Use case resumes at step 2. +**Actors: Staff** -*{More to be added}* +**MSS** -### Non-Functional Requirements +1. Staff requests to exits the program. +2. ezFoodie exits. -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. + Use case ends. -*{More to be added}* +**Extensions** -### Glossary +* 1a. The given input is invalid. -* **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others + * 1a1. ezFoodie shows an error message. --------------------------------------------------------------------------------------------------------------------- + Use case ends. -## **Appendix: Instructions for manual testing** +Use case: - List out all members -Given below are instructions to test the app manually. +**Actors: Staff** -
:information_source: **Note:** These instructions only provide a starting point for testers to work on; -testers are expected to do more *exploratory* testing. +**MSS** -
+1. Staff requests to list out all members. +2. ezFoodie shows a list of members. -### Launch and shutdown + Use case ends. -1. Initial launch +**Extensions** - 1. Download the jar file and copy into an empty folder +* 1a. The given input is invalid. - 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. + * 1a1. ezFoodie shows an error message. -1. Saving window preferences + Use case ends. - 1. Resize the window to an optimum size. Move the window to a different location. Close the window. +* 2a. The member list is empty. - 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained. + Use case ends. + +**Use Case: UC04 - Add new member** + +**Actors: Staff** -1. _{ more test cases …​ }_ +**MSS** -### Deleting a person +1. Staff requests to add the information of a member to the member list. +2. The member is added in ezFoodie with the given information. -1. Deleting a person while all persons are being shown + Use case ends. - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. +**Extensions** - 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. +* 1a. The given input is invalid. - 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. + * 1a1. ezFoodie shows an error message. + + Use case ends. - 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous. +* 1b. The member (phone or email) already exists in the member list. -1. _{ more test cases …​ }_ + * 1b1. ezFoodie shows an error message. -### Saving data + Use case ends. + +Use case: - find a member by [field] + +**Actors: Staff** + +**MSS** + +1. Staff requests to find a member by [field] with keywords, [field] can be member ID, name, phone, email or registration date. +2. ezFoodie shows a list of members whose [field] matches the keywords. + + Use case ends. + +**Extensions** + +* 1a. The given input is invalid. + + * 1a1. ezFoodie shows an error message. + + Use case ends. + +* 2a. The member list is empty. + + Use case ends. + +**Use case: UC06 - Show member profile** + +**Actors: Staff** + +**MSS** + +1. Staff requests to list a set of members (UC03 or UC05). +2. Staff requests to view a specific member in the member list by member ID. +3. ezFoodie shows the specific member's details. + + Use case ends. + +**Extensions** + +* 2a. The given input is invalid. + + * 2a1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2b. The given member ID does not exist in the member list. + + * 2b1. ezFoodie shows an error message. + + Use case resumes at step 1. + +**Use case: UC07 - Add transaction by member ID** + +**Actors: Staff** + +**MSS** + +1. Staff requests to list a set of members (UC03 or UC05). +2. Staff requests to add transaction details for a member by member ID. +3. The transaction of the member is added in ezFoodie. + + Use case ends. + +**Extensions** + +* 2a. The given input is invalid. + + * 2a1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2b. The given member ID does not exist in the member list. + + * 2b1. ezFoodie shows an error message. + + Use case resumes at step 1. + +**Use case: UC08 - Redeem member’s points by [field]** + +**Actors: Staff** + +**MSS** + +1. Staff requests to list a set of members (UC03 or UC05). +2. Staff requests to redeem one gift (e.g. 1 item = 100 points) for a member by [field], [field] can be member ID or index number. +3. The points of the member are deducted (e.g. -100 points) in ezFoodie. + + Use case ends. + +**Extensions** + +* 2a. The given input is invalid. + + * 2a1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2b. The given [field] does not exist in the member list. + + * 2b1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2c. The member does not have enough points. + + * 2c1. ezFoodie shows an error message. + + Use case resumes at step 1. + +**Use case: UC09 - Add reservation by member ID** + +**Actors: Staff** + +**MSS** + +1. Staff requests to list a set of members (UC03 or UC05). +2. Staff requests to add reservation for the member by member ID. +3. A reservation date time and remark of the member are added in ezFoodie. + + Use case ends. + +**Extensions** + +* 2a. The given input is invalid. + + * 2a1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2b. The given member ID does not exist in the member list. + + * 2b1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2c. The reservation date time is not after the current date time. + + * 2c1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2d. Multiple reservations on the same day are added for the same member. + + * 2d1. ezFoodie shows an error message. + + Use case resumes at step 1. + +**Use case: UC10 - Clear the program** + +**Actors: Manager** + +**Preconditions** + +Manager is logged in + +**MSS** + +1. Manager requests to clear and initialize the entire program. +2. All the data in ezFoodie is removed. + + Use case ends. + +**Extensions** + +* 1a. The given input is invalid. + + * 1a1. ezFoodie shows an error message. + + Use case ends. + +**Use case: UC11 - Login as a manager** + +**Actors: Manager** + +**MSS** + +1. Manager requests to login as a manager. +2. ezFoodie switch to Manager Mode. + + Use case ends. + +**Extensions** + +* 1a. The given input is invalid. + + * 1a1. ezFoodie shows an error message. + + Use case ends. + +* 1b. The password is not correct. + + * 1b1. ezFoodie shows an error message. + + Use case ends. + +**Use case: UC12 - Logout as a manager** + +**Actors: Manager** + +**Preconditions** + +Manager is logged in + +**MSS** + +1. Manager requests to logout as a manager. +2. ezFoodie switch to Normal Mode. + + Use case ends. + +**Extensions** + +* 1a. The given input is invalid. + + * 1a1. ezFoodie shows an error message. + + Use case ends. + +**Use case: UC13 - Update login password** + +**Actors: Manager** + +**Preconditions** + +Manager is logged in + +**MSS** + +1. Manager requests to update the login password. +2. A new login password is updated in ezFoodie. + + Use case ends. + +**Extensions** + +* 1a. The given input is invalid. + + * 1a1. ezFoodie shows an error message. + + Use case ends. + +**Use case: UC14 - Sort members by credit in [field]** + +**Actors: Manager** + +**Preconditions** + +Manager is logged in + +**MSS** + +1. Manager requests to sort members by credit in [field], [field] can be ascending order or descending order. +2. ezFoodie shows a list of members sorted by credit in [field]. + + Use case ends. + +**Extensions** + +* 1a. The given input is invalid. + + * 1a1. ezFoodie shows an error message. + + Use case ends. + +* 2a. The member list is empty. + + Use case ends. + +**Use case: UC15 - Edit member [field1] by [field2]** + +**Actors: Manager** + +**Preconditions** + +Manager is logged in + +**MSS** + +1. Manager requests to list a set of members (UC03 or UC05). +2. Manager requests to edit member [field1] by [field2], [field1] can be name, phone, email or address, [field2] can be member ID or index number. +3. ezFoodie shows the updated information of the member. + + Use case ends. + +**Extensions** + +* 2a. The given input is invalid. + + * 2a1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2b. The given [field2] does not exist in the member list. + + * 2b1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2c. The new phone or email already exists in the member list. + + * 2c1. ezFoodie shows an error message. + + Use case resumes at step 1. + +**Use case: UC16 - Delete member by [field]** + +**Actors: Manager** + +**Preconditions** + +Manager is logged in + +**MSS** + +1. Manager requests to list a set of members (UC03 or UC05). +2. Manager requests to delete the member by [field], [field] can be member ID or index number. +3. The member is deleted from ezFoodie. + + Use case ends. + +**Extensions** + +* 2a. The given input is invalid. + + * 2a1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2b. The given [field] does not exist in the member list. + + * 2b1. ezFoodie shows an error message. + + Use case resumes at step 1. + +**Use case: UC17 - Edit reservation by member ID and reservation ID** + +**Actors: Staff** + +**MSS** + +1. Staff requests to list a set of members (UC03 or UC05). +2. Staff requests to edit reservation by member ID and reservation ID. +3. The reservation of the member is updated in ezFoodie. + + Use case ends. + +**Extensions** + +* 2a. The given input is invalid. + + * 2a1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2b. The given member ID does not exist in the member list. + + * 2b1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2c. The given reservation ID does not exist in the member with the given member ID. + + * 2c1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2d. The reservation date time is not after the current date time. + + * 2d1. ezFoodie shows an error message. + + Use case resumes at step 1. + +**Use case: UC18 - Delete reservation by member ID and reservation ID** + +**Actors: Manager** + +**Preconditions** + +Manager is logged in + +**MSS** + +1. Manager requests to list a set of members (UC03 or UC05). +2. Manager requests to delete reservation by member ID and reservation ID. +3. The reservation of the member is deleted from ezFoodie. + + Use case ends. + +**Extensions** + +* 2a. The given input is invalid. + + * 2a1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2b. The given member ID does not exist in the member list. + + * 2b1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2c. The given reservation ID does not exist in the member with the given member ID. + + * 2c1. ezFoodie shows an error message. + + Use case resumes at step 1. + +**Use case: UC19 - Retrieve previous commands** + +**Actors: Staff** + +**MSS** + +1. Staff requests to retrieve previous commands by pressing the `up` or `down` key on the keyboard. +2. The previous commands saved in the history (up to 30) will be shown in the command box one by one. + + Use case ends. + +**Extensions** + +* 1a. 30 commands saved in the history, and `up` key is pressed more than 30 times continuously from the beginning. + + * 1a1. ezFoodie shows empty in the command box. + + Use case resumes at step 1. + +* 1b. 30 commands saved in the history, and `down` key is pressed more than 30 times continuously from the beginning. + + * 1b1. ezFoodie shows empty in the command box. + + Use case resumes at step 1. + +* 1c. No more than 30 commands saved in the history, and `up` key is pressed more than the number of stored commands history continuously from the beginning. + + * 1c1. ezFoodie shows empty in the command box. + + Use case resumes at step 1. + +* 1d. No more than 30 commands saved in the history, and `down` key is pressed more than the number of stored commands history continuously from the beginning. + + * 1d1. ezFoodie shows empty in the command box. + + Use case resumes at step 1. + +**Use case: UC20 - Edit transaction by member ID and transaction ID** + +**Actors: Staff** + +**MSS** + +1. Staff requests to list a set of members (UC03 or UC05). +2. Staff requests to edit transaction by member ID and transaction ID. +3. The transaction of the member is updated in ezFoodie. + + Use case ends. + +**Extensions** + +* 2a. The given input is invalid. + + * 2a1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2b. The given member ID does not exist in the member list. + + * 2b1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2c. The given transaction ID does not exist in the member with the given member ID. + + * 2c1. ezFoodie shows an error message. + + Use case resumes at step 1. + +**Use case: UC21 - Delete transaction by member ID and transaction ID** + +**Actors: Manager** + +**Preconditions** + +Manager is logged in + +**MSS** + +1. Manager requests to list a set of members (UC03 or UC05). +2. Manager requests to delete transaction by member ID and transaction ID. +3. The transaction of the member is deleted from ezFoodie. + + Use case ends. + +**Extensions** + +* 2a. The given input is invalid. + + * 2a1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2b. The given member ID does not exist in the member list. + + * 2b1. ezFoodie shows an error message. + + Use case resumes at step 1. + +* 2c. The given transaction ID does not exist in the member with the given member ID. + + * 2c1. ezFoodie shows an error message. + + Use case resumes at step 1. + + +**Use case: UC22 - View summary for registrations and transactions** + +**Actors: Manager** + +**Preconditions** + +Manager is logged in + +**MSS** + +1. Manager requests to view summary for member registrations and transactions details. +2. ezFoodie shows a list of statistics including total number of member registrations, total number and amount of transactions in past months. + + Use case ends. + +**Extensions** + +* 1a. The given input is invalid. + + * 1a1. ezFoodie shows an error message. + + Use case ends. + + +*{More to be added}* + +### Non-Functional Requirements + +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 members without a noticeable sluggishness in performance for typical usage. +3. Should be able to hold up to 5000 transactions without a noticeable sluggishness in performance for typical usage. +4. 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. +5. The application should not exceed 50mb in size. +6. The documentation should be easy to understand. + +*{More to be added}* + +### Glossary + +* **Mainstream OS**: Windows, Linux, Unix, OS-X +* **CSV File**: A comma-separated values file is a delimited text file that uses a comma to separate values. Each line of the file is a data record. Each record consists of one or more fields, separated by commas. And it can be opened using Microsoft Excel (Ref: https://en.wikipedia.org/wiki/Comma-separated_values) +* **JSON file**: A file that uses human-readable text to store and transmit data objects consisting of attribute–value pairs and arrays (or other serializable values) (Ref: https://en.wikipedia.org/wiki/JSON) +* **Staff**: All employees of the restaurant who will be using the product +* **Manager**: A special subset of staff with higher permission who can get special access to certain higher level features +* **Normal Mode**: The mode before the manager login, Normal Mode by default +* **Manager Mode**: The mode after the manager login +* **Membership Tiers**: Different membership tiers give members different benefits. Tiers include Bronze, Silver, Gold, and Platinum +* **Member Details**: Member ID, Name, Phone, Email, Address, Membership Tiers, Registration Date, Credits, Points, Transaction +* **Credits**: Represents total amount of money spent at the restaurant (S$1 = 1 credit), accumulated and cannot be decreased +* **Points**: Earned 1 points for S$1, can be used to redeem gifts and will be spent +* **Transaction**: A payment made by a customer at the restaurant +* **Redemption**: Points that a member has can be redeemed for free items, and the points will then be deducted from the member’s account +* **Reservation**: A tag contains specific date time and remark that represents when the member will come for a meal + +-------------------------------------------------------------------------------------------------------------------- + +## **Appendix 2: Instructions for manual testing** + +Given below are instructions to test the app manually. + +
:information_source: **Note:** These instructions only provide a starting point for testers to work on; +testers are expected to do more *exploratory* testing. + +
+ +### Launch and shutdown + +1. Initial launch + + 1. [Download the jar file](https://github.com/AY2122S1-CS2103T-F12-4/tp/releases) and copy into an empty folder + + 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. + +1. 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.
+ Expected: The most recent window size and location is retained. + +### Opening help window + +1. Opening help window + + 1. Test case: `help`
+ Expected: a help window will be popped up to display a list of commands and their respective format, and also a link to the user guide. + +### Listing out all members + +1. Listing out all members + + 1. Prerequisites: There are multiple members exist in ezFoodie. + + 1. Test case: `list -mem/`
+ Expected: all members with their own member ID, index number, name, tier, phone, email, credit, point and coming reservation (if exists) will be shwon. + +### Adding new member + +1. Adding a new member while all members are being shownn + + 1. Prerequisites: List all members using the `list -mem/` command. Multiple members in the member list. + + 1. Test case: `add -mem/ -n/John Doe -p/92345678 -e/johnd@example.com -a/311, Clementi Ave 2, #02-25`
+ Expected: A new member with name `John Doe`, phone `92345678`, email `johnd@example.com` and address `311, Clementi Ave 2, #02-25` is added to the member list. + + 1. Test case: `add -mem/ -n/John Bob -p/92345678 -e/johnb@example.com -a/113, Clementi Ave 3, #03-36`
+ Expected: No member is added because the phone `92345678` already exists in the member list. Status message shows that `This member (phone or email) already exists in the ezFoodie.`. + + 1. Test case: `add -mem/ -n/John Doe -p/92345678 -e/johnd@example.com`
+ Expected: No member is added because not all compulsory field is filled. Status message shows `Invalid command format!`. + + 1. Other incorrect adding member commands to try: `add -mem/ -n/John Doe`, `...` (where command is not in correct format)
+ Expected: Status message shows `Invalid command format!`. + +### Finding members by [field] + +1. Finding members while all members are being shown + + 1. Prerequisites: + 1. List all members using the `list -mem/` command. Multiple members are in the member list. + 1. The [field] can be member ID, name, phone, email or registration date. + + 1. Test case: `find -mem/ -id/00001`
+ Expected: Member with member ID `00001` will be displayed. + + 1. Test case: `find -mem/ -id/00001 00002`
+ Expected: Members with member ID `00001` and `00002` will be displayed. + + 1. Test case: `find -mem/ -n/Alex`
+ Expected: Members with name `Alex` will be displayed. + + 1. Test case: `find -mem/ -p/87438807`
+ Expected: Member with phone `87438807` will be displayed. + + 1. Prerequisites: member wih member ID `99999` is not in the member list. + 1. Test case: `find -mem/ -id/00001`
+ Expected: No member will be displayed. Status message shows `0 members listed!`. + + 1. Other incorrect adding member commands to try: `find -mem/`, `...` (where command is not in correct format)
+ Expected: Status message shows `Invalid command format!`. + +### Editing member [field] by member ID + +1. Editing a member while all members are being shown + + 1. Prerequisites: + 1. List all members using the `list -mem/` command. Multiple members are in the member list. + 1. The [field] can be name, phone or email. + + 1. Test case: `edit -mem/ -id/00002 -p/91234567 -e/berniceyu123@example.com`
+ Expected: The phone and email of the with member ID `00002` are updated to `91234567` and `berniceyu123@example.com`. + + 1. Prerequisites: member wih member ID `99999` is not in the member list. + 1. Test case: `edit -mem/ -id/99999 -p/92233445 -e/johndoe@example.com`
+ Expected: No member is updated. Status message shows `The member ID provided is invalid.`. + + 1. Test case: `edit -mem/ -id/00002 -p/92233445 -p/82233445 -e/berniceyu456@example.com`
+ Expected: The phone and email of the with member ID `00002` are updated to `82233445` and `berniceyu456@example.com`. + + 1. Prerequisites: member with phone `87438807` is in the member list. + 1. Test case: `edit -mem/ -id/00003 -p/87438807 -e/charlotte123@example.com`
+ Expected: No member is updated. Status message shows `This member (phone or email) already exists in the ezFoodie.`. + + 1. Other incorrect adding member commands to try: `edit -mem/ -id/00003`, `...` (where command does not contain any fields for editing)
+ Expected: Status message shows `At least one field to edit must be provided.`. + +### Showing member profile + +1. Showing a member profile + + 1. Prerequisites: + 1. Member wih member ID `00001` is in the member list. + 1. Member wih member ID `99999` is not in the member list. + + 1. Test case: `show -mem/ -id/00001`
+ Expected: A summary window will be popped up to display the details of the member with member ID `00001`. + + 1. Test case: `show -mem/ -id/99999`
+ Expected: No member details will be shown. Status message shows `The member ID provided is invalid.`. + + 1. Other incorrect delete commands to try: `show -mem/`, `...` (where command is not in correct format)
+ Expected: Status message shows `Invalid command format!`. + +### Logging in as a manager + +1. Logging in as a manager + + 1. Prerequisites: Login password is `123456`. + + 1. Test case: `login 123456`
+ Expected: Logged in successfully and the role shown on bottom right corner of the program will become `MANAGER`. + + 1. Test case: `login 654321`
+ Expected: Failed to login and the role shown on bottom right corner of the program is still `STAFF`. + +### Adding transaction by member ID + +1. Adding transaction by member ID + + 1. Prerequisites: + 1. Member wih member ID `00001` is in the member list. + 1. [Showing member profile with member ID `00001`](#showing-member-profile). + + 1. Test case: `add -txn/ -b/23.00 -id/00001`
+ Expected: A new transaction with billing `23.00` is added to the transaction list of the member with member ID `00001`, which is shown in the member profile. Details of the added transaction shown in the status message. + + 1. Test case: `add -txn/ -b/99999999.00 -id/00001`
+ Expected: No transaction will be added. Status message shows `Billings should be non-negative numeric with 2 decimal places, and max amount is 9999.99.`. + + 1. Other incorrect delete commands to try: `add -txn/ -b/123.00`, `...` (where command is not in correct format)
+ Expected: Status message shows `Invalid command format!`. + +### Adding reservation by member ID + +1. Adding reservation by member ID + + 1. Prerequisites: + 1. Member wih member ID `00001` is in the member list. + 1. [Showing member profile with member ID `00001`](#showing-member-profile). + 1. Current date time should be before A `2021-12-01 13:00`, can be changed to any future date time if A is reached. + 1. Current date time should be after B `2020-12-01 13:00`. + + 1. Test case: `add -rs/ -dt/2021-12-01 13:00 -rm/2 people -id/00001`
+ Expected: A new reservation with date time `2021-12-01 13:00` and remark `2 people` is added to the reservation list of the member with member ID `00001`, which is shown in the member profile. Details of the added reservation shown in the status message. + + 1. Test case: `add -rs/ -dt/2020-12-01 13:00 -rm/2 people -id/00001`
+ Expected: No reservation will be added. Status message shows `The given reservation date time should be after current date time.`. + + 1. Test case: `add -rs/ -dt/2021-12-01 14:00 -rm/2 people -id/00001`, this date also need to be changed to be the same as A if A is changed
+ Expected: No reservation will be added. Status message shows `Only one reservation can be added within the same day.`. + + 1. Other incorrect delete commands to try: `add -txn/ -b/123.00`, `...` (where command is not in correct format)
+ Expected: Status message shows `Invalid command format!`. + +### Sorting members by credit in [field] + +1. Sorting members by credit in [field], [field] can be ascending order or descending order. + + 1. Prerequisites: + 1. List all members using the `list -mem/` command. Multiple members in the member list. + 1. [Logged in as a manager](#logging-in-as-a-manager). + + 1. Test case: `sort -mem/ -c/ -a/`
+ Expected: the members are sorted by credit in ascending order. + + 1. Test case: `sort -mem/ -c/ -d/`
+ Expected: the members are sorted by credit in descending order. + + 1. Other incorrect delete commands to try: `sort -mem/`, `...` (where command is not in correct format)
+ Expected: Status message shows `Invalid command format!`. + +### Deleting transaction by member ID and transaction ID + +1. Deleting transaction by member ID + + 1. Prerequisites: + 1. Member wih member ID `00001` is in the member list. + 1. Multiple transactions are in the transaction list of the member with member ID `00001`. + 1. [Showing member profile with member ID `00001`](#showing-member-profile). + + 1. Test case: `del -txn/ -id/00001000001`
+ Expected: Transaction with transaction ID `000001` in the transaction list of the member with member ID `00001` is deleted. Details of the deleted transaction shown in the status message. + + 1. Prerequisites: member wih member ID `99999` is not in the member list. + 1. Test case: `del -txn/ -id/99999000001`
+ Expected: No transaction will be deleted. Status message shows `The member ID provided is invalid.`. + + 1. Prerequisites: transaction wih transaction ID `999999` is not in the transaction list. + 1. Test case: `del -txn/ -id/00001999999`
+ Expected: No transaction will be deleted. Status message shows `The transaction ID provided is invalid.`. + +### Deleting reservation by member ID and reservation ID + +1. Deleting reservation by member ID + + 1. Prerequisites: + 1. Member wih member ID `00001` is in the member list. + 1. Multiple reservations are in the reservation list of the member with member ID `00001`. + 1. [Showing member profile with member ID `00001`](#showing-member-profile). + + 1. Test case: `del -rs/ -id/00001000001`
+ Expected: Reservation with reservation ID `000001` in the reservation list of the member with member ID `00001` is deleted. Details of the deleted reservation shown in the status message. + + 1. Prerequisites: member wih member ID `99999` is not in the member list. + 1. Test case: `del -rs/ -id/99999000001`
+ Expected: No reservation will be deleted. Status message shows `The member ID provided is invalid.`. + + 1. Prerequisites: reservation wih reservation ID `999999` is not in the reservation list. + 1. Test case: `del -rs/ -id/00001999999`
+ Expected: No reservation will be deleted. Status message shows `The reservation ID provided is invalid.`. + +### Deleting member by member ID + +1. Deleting a member while all members are being shown + + 1. Prerequisites: + 1. List all members using the `list -mem/` command. Multiple members in the member list. + 1. [Logged in as a manager](#logging-in-as-a-manager) + + 1. Test case: `del -mem/ -id/00001`
+ Expected: member with member ID `00001` is deleted from the member list. Details of the deleted contact shown in the status message. + + 1. Test case: `del -mem/ -id/000001`
+ Expected: No member is deleted. Status message shows `Member IDs should only contain 5 digits and it should not be blank, and max ID is 99999`. + + 1. Do not have member with member ID `99999` + 1. Test case: `del -mem/ -id/99999`
+ Expected: No member is deleted. Status message shows `The member ID provided is invalid.`. + +### Clearing the program + +1. Clearing the program + + 1. Test case: `clear`
+ Expected: All data will be cleared. + +### Exiting the program + +1. Exiting the program + + 1. Test case: `exit`
+ Expected: The window of the program will close. + +### Saving data + +1. Saving data and restoring data when it is cleared by accident + + 1. Prerequisites: + 1. List all members using the `list -mem/` command. Multiple members in the member list. + 1. Locate the ezfoodie JSON file at the default location: `[JAR file location]/data/ezFoodie.json`. + 1. Back up the ezfoodie JSON file. + + 1. Test case: [Clear the program](#clearing-the-program), [Exit the program](#exiting-the-program), replace the current `[JAR file location]/data/ezFoodie.json` with the backed up ezfoodie JSON file, then relaunch the program
+ Expected: All the backed up data is restored. + +## **Appendix 3: Effort** + +If the effort required to create **[Address Book](https://se-education.org/addressbook-level3)** is 10, we would place the effort level required to implement the current version of **ezFoodie** at 20. + +Our team has put in a significant amount of effort to get ezFoodie to the current version. Below, we list out the summany of the enhancement and extension implemented by us. + +### Enhancement and Extension + +1. **Features enhancement** + + As **[Address Book](https://se-education.org/addressbook-level3)** has undergone complete testing and a large number of iterations, we follow its design and architecture which allows us to ensure the robustness and stability of the program with great confidence. + + Although we reused the basic features (e.g., `add`, `edit`, `delete`, and `list`), we still put much more effort to enhance these features according to the requirements of our program. For example, we can now do `add`, `edit` and `delete` operations on mutliple data models, which are `Member`, `Transaction` and `Reservation`. + +2. **Features extension** + + We realized that the basic features in **[Address Book](https://se-education.org/addressbook-level3)** are not able to fully meet our usage scenarios, so we also put a lot of effort to extend other features that are more suitable for our program. + + First we added `Account`, `Transaction` and `Reservation` model for role (manager and staff) management, transaction management and reservation management respectively. + + Then we added `login`, `logout` to support role switching, `sort`, `show` and `summary` to support the statistics of current members and transactions, etc. + + Lastly, we had to integrate all these new features with the existing code to become this powerful ezFoodie aaplication! 🤩 + + The implemention details and design considerations for these features could be found in [Implementation](#implementation) section. + +3. **UI extension** + + The UI is designed based on **[Address Book](https://se-education.org/addressbook-level3)**. We follow the principle of its neat and simple design style to ensure the principle of ease-of-use. + + In addition, Compared to **[Address Book](https://se-education.org/addressbook-level3)**, ezFoodie has much more powerful display capabilities, it can display more details through multiple windows, such as displays the member details by `show -mem/ -id/00001` command, and displays the statistics of current members and transactions by `summary` command. + +## **Appendix 4: Limitations and Future improvements** + +We acknowledge the fact that our current product is not perfect, and it still has rooms for improvement. +Below are some limitations and future improvements of our product. + +### Limitations + +1. **Automatic reminder feature is not implemented** + + The program only displays the most recent reservation of each member on the main view, but it does not support the functionality of reminding managers and staffs automatically when the members are comming. When there are a large number of users and a large number of reservations, the ability of managers and staffs to arrange seats and menus for upcoming members in a timely and efficient manner is an important manifestation of the service capabilities of the restaurant, so this feature has a potential positive impact on the growth of restaurant members. + +1. **Undo/redo features are not implemented** + + Having the undo/redo feature can better improve use efficiency and fault tolerance due to fast typing. It is very important and necessary optimization that the user can recover the data in time if the user accidentally performs the wrong operation. + +1. **Pagination feature is not implemented** + + As the number of members increases, displaying all members on one page by `list -mem/` is very unuser-friendly. If the managers and staffs needs to scroll to view a large amount of information, it will go against the original intention of the project itself: to improve the efficiency of managing members through fast typing. + +### Future Improvements + +1. **Add a timer thread** + + A timer thread can be added in the background of the application to keep track of all the reservations of members. When an upcoming reservation is detected, a prompt dialog will be popped up or the relevant member reservation information will be displayed in the upper right corner of the application. + +1. **Implement by following the proposed [undo/redo](#udordo)** -1. Dealing with missing/corrupted data files +1. **Add a command shortcut [-pg/] for pagination** - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ + By default, only 20 records are displayed at a time, the user and a parameter [-pg/] is added to the paging function, so that users can turn pages by typing quickly. -1. _{ more test cases …​ }_ + Continue to implement the current limitations mentioned above to make the program more user-friendly. diff --git a/docs/Documentation.md b/docs/Documentation.md index 3e68ea364e7..292ddcbda04 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -10,7 +10,7 @@ title: Documentation guide * To learn how set it up and maintain the project website, follow the guide [_[se-edu/guides] **Using Jekyll for project documentation**_](https://se-education.org/guides/tutorials/jekyll.html). * Note these points when adapting the documentation to a different project/product: * The 'Site-wide settings' section of the page linked above has information on how to update site-wide elements such as the top navigation bar. - * :bulb: In addition to updating content files, you might have to update the config files `docs\_config.yml` and `docs\_sass\minima\_base.scss` (which contains a reference to `AB-3` that comes into play when converting documentation pages to PDF format). + * :bulb: In addition to updating content files, you might have to update the config files `docs\_config.yml` and `docs\_sass\minima\_base.scss` (which contains a reference to `ezFoodie` that comes into play when converting documentation pages to PDF format). * If you are using Intellij for editing documentation files, you can consider enabling 'soft wrapping' for `*.md` files, as explained in [_[se-edu/guides] **Intellij IDEA: Useful settings**_](https://se-education.org/guides/tutorials/intellijUsefulSettings.html#enabling-soft-wrapping) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3716f3ca8a4..dfc42088f26 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -3,38 +3,206 @@ 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. +
ezFoodie Logo
* Table of Contents {:toc} -------------------------------------------------------------------------------------------------------------------- -## Quick start +## Introduction + +Welcome to ezFoodie’s User Guide! 🤩 + +Have you faced issues when managing the members of your restaurant? ezFoodie is here! + +ezFoodie is a desktop application that helps restaurants **keep track of their ever-growing list of members**. Restaurant managers and staff can easily view and update member statuses (e.g. personal information, tier, reservation, transaction, etc.) to manage and analyze members. -1. Ensure you have Java `11` or above installed in your Computer. +ezFoodie is simple and user-friendly. It is optimized for using via a **Command Line Interface (CLI)** while still enjoying the benefits of a Graphical User Interface (GUI). If you can type fast, ezFoodie can get your member management tasks done faster than traditional GUI applications. -1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +ezFooide's current version is optimized for the **English language based user**, i.e, all CLI commands are **English-based**. As of now ezFoodie does not support other languages. + +Continue reading to explore the wonders of ezFoodie and enhance the way you manage and analyze your restaurant's members 🤩 + +-------------------------------------------------------------------------------------------------------------------- -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +## Purpose -1. Double-click the file to start the app. The GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.png) +**This User Guide aims to:** -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: +1. Help new users get familiar with ezFoodie. - * **`list`** : Lists all contacts. +2. Provide a summary of all available commands in ezFoodie and their usages. - * **`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. +3. Show detailed explanations of all commands and possible issues users may face. - * **`delete`**`3` : Deletes the 3rd contact shown in the current list. +4. Frequently asked questions and answers. - * **`clear`** : Deletes all contacts. +-------------------------------------------------------------------------------------------------------------------- - * **`exit`** : Exits the app. +## Quick start -1. Refer to the [Features](#features) below for details of each command. +**** + * To check out the Java version you have installed in your computer, please click [here](https://www.java.com/en/download/help/version_manual.xml). + * If Java is not installed on your computer, please follow this [link](https://www.oracle.com/java/technologies/downloads/). +
+ +**2. Download the latest version of `ezfoodie.jar` from [here](https://github.com/AY2122S1-CS2103T-F12-4/tp/releases).** + +
downloadInstruction
+
+ +**3. Copy the file to the folder you want to use as the _home folder_ for your ezFoodie.** + +
homefolder
+
+ +**4. Launch the application and view the details.** +* Double-click the file to start the application in windows system. +* If any issues are faced, go back to [step 1](#step1) and check Java version. +* In MacOS or Linux System, launch the termianl, go to the directory you saved `ezfoodie.jar` in, and key in the `java -jar ezfoodie.jar` command.* You can find details on how to run the JAR file [here](https://www.wikihow.com/Run-a-.Jar-Java-File). +* The GUI similar to the one shown below should appear in a few seconds. The application comes preloaded with sample data to test its features.
+ +
Launch
+

Congratulation! ezFoodie is now ready for your perusal. :+1:

+
+ +**5. This section will walk you through the layout of ezFoodie.** +

Layout of Main Window

+
UIMarkShow
+ +

Layout of Help Window

+
MarkHelp
+ +

Layout of Show Window

+
MarkShow
+ +

Layout of Summary Window

+
MarkSummary
+
+ +**For more details, can refer to [Features](#features).** + +**6. Main window GUI includes the following member fields and constraints:** + +* **Index number in the list:** + * *Only contains alphanumeric characters.* + * *It ranges from `1` to `99999`.* + +* **Name:** + * *Only contains alphanumeric characters and spaces, and it should not be blank.* + +* **Tier:** + * *Bronze, Silver, Gold, Platinum.* + * *This is automatically calculated from a member's credit.* + * *Upgrade of Tier (e.g. Bronze -> Silver) is also automatically handled by ezFoodie.* + +* **Member ID:** + * *Member ID has only 5 digits' numerical value and starts from `00001` to `99999`.* + * *Deleted Member's id will not be occupied when adding a new member.* + * *Member id will increase automatically when a new member is added.* + * *EzFoodie contains max `99999` members.* + +* **Phone number:** + * *Phone number should only contain 8 digits, and it should start with 3, 6, 8 or 9.* + * *Members cannot share the same phone number or email. ezFoodie uses these fields to detect duplicate members.* + +* **Email:** + * *Email should be of the format in `local-part@domain`.* + * *The `local-part` should only contain alphanumeric characters and + these special characters: `+ _ . - `, the `local-part` may not start or end with + any special characters.* + * *The `domain` name is made up of domain name separated by periods.* + * *The `domain` name must:* + * *End with a `domain` label at least 2 characters long.* + * *Have each domain label start and end with alphanumeric characters.* + * *Have each domain label consist of alphanumeric characters, + separated only by hyphens, if any.* + * *Members cannot share the same phone number or email. ezFoodie uses these fields to detect duplicate members.* + +* **Credit:** + * *Credit refers to the overall accumulated transaction billing amounts of a member, + starting from their registration date.* + * *Credit depends on the billing amounts in [add transaction](#12), [delete transaction](#13), and [edit transaction](#14)*. + * *Credit amount ranges from `0` to `99999999`.* + +* **Point:** + * *The point accumulation is similar to credit.* + * *However, point can be redeemed as discounts to a member, and point will be deducted + accordingly with redemption.* + * *In delete transaction, the point will not be affected and will stay the same.* + * *In edit transaction, the point will be increased when billing amount is greater than the billing amount added in last time. On the contrary the point will not be affected and keep the same when billing amount is lesser than the billing amount added in last time.* + * *Point amount is range from `0` to `99999999`.* + +* **Coming Seat Booking Information:** + * Seats can be reserved using the [add reservation](#15) feature. + * A reservation can be edited or deleted as well using the [edit reservation](#17) and [delete reservation](#16) features. + * The maximum amount of reservations is `999999`. + +**7. Type the command in the command box and press Enter to execute it. Some example commands you can try here:** + + * `help`: Opens the help window to view the commands and user guide. + + * `login 123456`: Changes user permissions from staff permissions to manager permissions. + + * `logout`: Logs out of manager and changes permissions to staff permissions. + + * `add -mem/ -n/John Doe -p/98765432 -e/johndoe@gmail.com -a/112 Amoy Street, 069907, Singapore`: Adds a contact named `John Doe` to the member list. + + * `list -mem/`: Lists all members. + + * `del -mem/ -i/3`: Deletes the member with index number 3 shown in the current list. +
+ + :information_source: **Note:** Only managers can delete members. Login as a manager before entering the delete command. +
+ * `add -txn/ -b/200.00 -id/00001`: Adds transaction with a bill amount of $200.00 to member id 00001 shown in the current list. + + * `add -rs/ -dt/2021-12-25 00:00 -rm/2 people -id/00001`: Adds a reservation for 2 people for 2021-01-01 00:00 to member ID 00001 shown in the current list + + * `show -mem/ -id/00001`: View all the details of a member, including all their transactions and reservations. + + * `redeem -rd/100 -id/00001`: Redeems 100 point from member id 00001 which is shown in the current list. + + * `set -pass/ 123456`: Sets the password to 123456 as in manager mode. + + * `summary`: Views a summary of all the data in the application in one page (**e.g.** No. of members, Past transactions). +
+ + :information_source: **Note:** Only managers are allowed to view the summary of data. Login as a manager before entering the summary command. +
+ + * `clear`: Clears the program. + + * `exit`: Exits the program. + +**8. Please refer to the [Features](#features) below for details of each command.** + +**9. Other components and constraints:** + + * **Transaction ID:** + * *It only has 6 digits and starts from `000001` to `999999`.* + * *Deleted Transaction ID's will not be occupied when adding a new transaction.* + * *Transaction id will increase automatically when adding a new transaction.* + + * **Bill amount:** + * *Bill amount is in the transaction feature.* + * *Bill amounts should be non-negative numeric with 2 decimal places, and ranges from `0.00` to `9999.99`.* + + * **Reservation ID:** + * *It only has 6 digits and starts from `000001` to `999999`.* + * *Deleted reservation ids will not be occupied when adding a new member.* + * *Reservation id will increase automatically when adding a new reservation.* + + * **Address:** + * *Address can take any values. It should not be blank.* + + * **DateTime:** + * *The format should be in yyyy-MM-dd HH:mm.* + +* **Please follow the instructions shown in the application command box when you are facing constraint errors (attached below).** +![popup_error_example](images/popup_error_example.png) +
-------------------------------------------------------------------------------------------------------------------- @@ -42,151 +210,558 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo
-**:information_source: Notes about the command format:**
+**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`. + e.g. in `find -mem/ -n/`, `NAME` is a parameter which can be used as `find -mem/ -n/John Doe`. -* 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`. +* If a parameter is expected only once in the command, but you specified it multiple times, only the last occurrence of the parameter will be taken.
+ **e.g.** if you specify `-p/12341234 -p/56785678`, only `-p/56785678` will be taken. -* 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. +* Extraneous parameters for commands that do not take in parameters (such as `help`, `exit` and `clear`) will be ignored.
+ **e.g.** if the command specifies `help 123`, it will be interpreted as `help`. -* 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. +* Meanings of `prefix`: -* If a parameter is expected only once in the command but you specified it multiple times, only the last occurrence of the parameter will be taken.
- e.g. if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken. + * `-id/`: member id, transaction id or reservation id -* 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`. + * `-i/`: index number of a member in member list -
+ * `-mem/`: member + + * `-n/`: name + + * `-p/`: phone number + + * `-e/`: email id + + * `-a/`: address -### Viewing help : `help` + * `-txn/`: transaction + + * `-rs/`: reservation + + * `-dt/`: dateTime + + * `-d/`: date + + * `-c/`: credit + + * `-b/`: billing amount + + * `-rm/`: remark + + * `-rd/`: redeem + + * `-pass/`: password + + * `-tag/`: tag + + *ONLY USED IN SORT COMMAND* + + * `-a/`: ascending + + * `-d/`: descending + +* **Requirements of input commands:** + * When the user inputs a command, the prefixes is not to be inputted inside of the specified content. + * E.g. The command "112`-p/` Amoy Street`-e/`, 069907, Singapore" is not allowed. + +
-Shows a message explaning how to access the help page. +### 1. Viewing help : `help` -![help message](images/helpMessage.png) +Opens a new window to show how to use the commands, and a link to the User Guide. Format: `help` +Example: `help` -### Adding a person: `add` +### 2. Exiting the program : `exit` -Adds a person to the address book. +Exits the program. + +Format: `exit` + +Example: `exit` + +### 3. Logging in as a manager : `login` + +Logs in as a manager. + +Format: `login ` + +*Default Manager Password is `123456`* + +Example: `login 123456` + +### 4. Setting and updating the password : `set` + +Sets and updates the password to login as manger. + +Format: `set -pass/` + +Example: `set -pass/123456` +
-Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` + :information_source: **Note:** The `set` command can only be used in manager mode. +
+ +### 5. Logging out as a manager : `logout` + +Logs out as a manager. + +Format: `logout` -
:bulb: **Tip:** -A person can have any number of tags (including 0) +Example: `logout` +
+ + :information_source: **Note:** The `logout` command can only be used in manager mode.
+### 6. Listing out a certain number of members : `list -mem/` + +Lists out all members. -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` +Format: `list -mem/` + +Example: `list -mem/` + +### 7. Adding a member : `add -mem/` + +Adds a new member to the member list. + +Format: `add -mem/ -n/ -p/ -e/ -a/
` + +Example: `add -mem/ -n/John Doe -p/98765432 -e/johndoe@gmail.com -a/112 Amoy Street, 069907, Singapore` + +
+ + :information_source: **Note:**
+ + * ezFoodie will not allow duplicate members and new members to have the same **phone number** or **email address** as a previously existing member. + +
+ +### 8. Deleting a member : `del -mem/` + +Deletes a member from the member list. + +
+ + :information_source: **Note:** + * Only the manager is allowed to delete members. Login as manager before entering command. +
+ +#### 8.1. Deleting a member by member id + +Deletes a member using member id. + +Format: `del -mem/ -id/` + +Example: `del -mem/ -id/00001` + +#### 8.2. Deleting a member by index number + +Deletes a member using member index number from the list. + +Format: `del -mem/ -i/` + +Example: `del -mem/ -i/1` + +### 9. Finding members : `find -mem/` + +Finds members by different fields which contain any of the given keywords. + + +
+ + :information_source: **Note:** + * No member will be listed when an invalid member field is entered. + * The find is case-insensitive. e.g. `hans` will match `Hans`. + * Only full words will be matched e.g. `Han` will not match `Hans`. + * Find command is only allowed for **one single prefix** after **-mem/**. + * In one single prefix find command, it can have multiple keywords. + * E.g. To find id:00001 and id:00002 members' detail: **"find -mem/ -id/00001 00002"**. + * It is not allowed to enter command as **"find -mem/ -id/00001 -p/98765432"**. +
+ +#### 9.1. Finding member by member id + +Format: `find -mem/ -id/` + +Example: `find -mem/ -id/00001` + +#### 9.2. Finding members by name + +Format: `find -mem/ -n/` + +Example: `find -mem/ -n/John Doe` + +#### 9.3. Finding members by phone + +Format: `find -mem/ -p/` + +Example: `find -mem/ -p/98765432` + +#### 9.4. Finding members by email + +Format: `find -mem/ -e/` + +Example: `find -mem/ -e/johndoe@gmail.com` + +#### 9.5. Finding members by registration date + +Format: `find -mem/ -d/` + +Example: `find -mem/ -d/2021-01-02` + +### 10. Showing member profile : `show -mem/` + +Shows member from member list by member id. + +Format: `show -mem/ -id/` + +Example: `show -mem/ -id/00001` + +
+ + :information_source: **Note:** No member details will be shown when an invalid member id is entered. +
+ +### 11. Editing a member : `edit -mem/` + + +Edits different fields in a member's profile, where the member is specified by member id. + +
+ + :information_source: **Note:** Only deals with editing member details. + If a member's transactions need to be edited, refer to [#14](#14) below. + Do not mix `-mem/` and `-txn/` in one `edit` command. +
-### Listing all persons : `list` +#### 11.1. Editing member name by member id -Shows a list of all persons in the address book. +Format: `edit -mem/ -id/ -n/` -Format: `list` +Example: `edit -mem/ -id/00001 -n/John Doe` -### Editing a person : `edit` +#### 11.2. Editing member phone by member id -Edits an existing person in the address book. +Format: `edit -mem/ -id/ -p/` -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +Example: `edit -mem/ -id/00001 -p/98765432` -* 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. +#### 11.3. Editing member email by member id -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. +Format: `edit -mem/ -id/ -e/` -### Locating persons by name: `find` +Example: `edit -mem/ -id/00001 -e/johndoe@gmail.com` -Finds persons whose names contain any of the given keywords. +#### 11.4. Editing member address by member id -Format: `find KEYWORD [MORE_KEYWORDS]` +Format: `edit -mem/ -id/ -a/
` -* 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` +Example: `edit -mem/ -id/00001 -a/33 Benoi Crescent, 629979, Singapore` -Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +#### 11.5. Editing member name by index number -### Deleting a person : `delete` +Format: `edit -mem/ -i/ -n/` -Deletes the specified person from the address book. +Example: `edit -mem/ -i/1 -n/John Doe` -Format: `delete INDEX` +#### 11.6. Editing member phone by index number -* 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, …​ +Format: `edit -mem/ -i/ -p/` -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. +Example: `edit -mem/ -i/1 -p/98765432` -### Clearing all entries : `clear` +#### 11.7. Editing member email by index number -Clears all entries from the address book. +Format: `edit -mem/ -i/ -e/` + +Example: `edit -mem/ -i/1 -e/johndoe@gmail.com` + +#### 11.8. Editing member address by index number + +Format: `edit -mem/ -i/ -a/
` + +Example: `edit -mem/ -i/1 -a/33 Benoi Crescent, 629979, Singapore` + + + +### 12. Adding transaction for members : `add -txn/` + +Adds transaction amount corresponding to member id. + +Format: `add -txn/ -b/ -id/` + +Example: `add -txn/ -b/200.00 -id/00001` + +
+ + :information_source: **Note:** + * Transaction date and time is automatically assigned by local date and time when a transaction is added, there is no need to manually enter the transaction date or time. + * The Credit and the Point will be accumulated based on billing amount. + * The maximum amount of transactions is `999999`. +
+ + + +### 13. Deleting transaction for members:`del -txn/` + +Deletes a transaction. + +Format: `del -txn/ -id/` + +Example: `del -txn/ -id/00001000001` + +
+ + :information_source: **Note:** + * Only the manager is allowed to delete transactions. Login as manager before entering command. + * Credit will be decreased based on bill amount in the deleted transaction. + * Points will not be affected and will stay the same. +
+ + + +### 14. Editing transaction of a member: `edit -txn/` + +Edits a member's transaction. + +Format: `edit -txn/ -id/ -b/` + +Example: `edit -txn/ -id/00001000002 -b/10.00` + +
+ +:information_source: **Note:** + * Transaction date and time cannot be modified once a transaction is added. + * Credit will be changed based on the bill amount of added transaction. + * Points will be increased when bill amount is greater than the bill amount + added in last time. On the contrary, points will not be affected and will stay the same when the bill amount is lesser + than the bill amount added in last time. +
+ + + +### 15. Adding a reservation: `add -rs/` + +Adds a reservation to a member. + +Format: `add -rs/ -dt/ -rm/ -id/` + +Example: `add -rs/ -dt/2021-01-02 00:00 -rm/2 people -id/00001` + +
+ +:information_source: **Note:** +* A member cannot make multiple reservations for the same day. +* The maximum amount of reservations is `999999`. +
+ + + +### 16. Deleting a reservation: `del -rs/` + +Deletes a reservation from a member's reservation list. + +Format: `del -rs/ -id/` + +Example: `del -rs/ -id/00001000001` + +
+ +:information_source: **Note:** + * Only the manager is allowed to delete reservations. Login as manager before entering command. +
+ + + +### 17. Editing a reservation: `edit -rs/` + +
+ +:information_source: **Note:** + * When editing the date time, the updated date time cannot share the same day as another reservation. + * E.g. when a member makes a reservation for 2021-12-01 13:00 and another for 2021-12-02 13:00, the member is not allowed to edit the date of the 2nd december reservation to 1st december. +
+ +#### 17.1. Editing the date time in reservation: `edit -rs/ -dt/` +Edits the date time of a member's reservation details. + +Format: `edit -rs/ -id/ -dt/` + +Example: `edit -rs/ -id/00001000001 -dt/2021-12-01 13:00` + +#### 17.2. Editing the remark in reservation: `edit -rs/ -rm/` +Edits the remark of a member's reservation details. + +Format: `edit -rs/ -id/ -rm/` + +Example: `edit -rs/ -id/00001000001 -rm/3 people` + +#### 17.3. Editing the date time and remark in reservation: `edit -rs/ -dt/ -rm/` +Edits the date time and remark member's reservation details. + +Format: `edit -rs/ -id/ [-dt/][-rm/]` + +Example: `edit -rs/ -id/00001000001 -dt/2021-12-01 13:00 -rm/3 people` + +### 18. Sorting members by credit : `sort -mem/` + +#### 18.1. Sorting member by credit in ascending order + +Format: `sort -mem/ -c/ -a/` + +Example: `sort -mem/ -c/ -a/` + +#### 18.2. Sorting member by credit in descending order + +Format: `sort -mem/ -c/ -d/` + +Example: `sort -mem/ -c/ -d/` + +### 19. Redeeming point `redeem -rd/` + +#### 19.1. Redeeming point for a member by member id: `redeem -rd/ -id/` + +Redeems the point of a member for a discount by member id. + +Format: `redeem -rd/ -id/` + +Example: `redeem -rd/100 -id/00001` + +#### 19.2. Redeeming point for a member by index: `redeem -rd/ -i/` + +Redeems the point of a member for a discount by index of member shown in the list. + +Format: `redeem -rd/ -i/` + +Example: `redeem -rd/100 -i/1` + +
+ +:information_source: **Note:** + * Multiple redemptions can be made from one `-rd/` command + * To redeem 100 points and 20 points together from member 00001: `redeem -rd/100 -rd/10 -id/00001`. +
+ +### 20. Summary: `summary` + +Views the summary of stored data in ezFoodie. + +
+ + :information_source: **Note:**: + * Only the manager is allowed to view summary. Login as manager before entering command. +
+ +Format: `summary` + +Example: `summary` + +### 21. Clearing ezFoodie data: `clear` + +Clears all data stored in ezFoodie. Use with Caution! Format: `clear` -### Exiting the program : `exit` +Example: `clear` +
-Exits the program. + :information_source: **Note:** + * Only managers are allowed to clear. Login as manager before entering command. +
-Format: `exit` -### Saving the data +### 22. Retrieving commands: `keyboard.UP` and `keyboard.DOWN` -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +Presses the keyboard "Up" button to retrieve previous command which has been entered, and "Down" button to retrieve next commands. -### Editing the data file +Format: press keyboard button ⬆️ and ⬇️ -AddressBook data are saved as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file. +
-
: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. + :information_source: **Note:** + * Retrieve commands can only store the history of 30 commands.
-### Archiving data files `[coming in v2.0]` -_Details coming soon ..._ +### 23. Exiting Summary, Help and Show popup window `keyboard.ESC` + +Presses the keyboard `ESC` button to exit Summary, Help and Show popup window. + +Format: press keyboard `ESC` + + + +### 24. Saving the data + +ezFoodie data are saved in a JSON file in hard disk `[JAR file location]/data/ezfoodie.json` automatically after any command that changes the data. There is no need to save manually. + +### 25. Editing the data file + +ezFoodie data are located at `[JAR file location]/data/ezfoodie.json`. Advanced users are welcome to update data directly by editing the data file. + +
:information_source: **Caution:** +If any changes to the JSON file renders it invalid, ezFoodie will discard all data and restart with an empty data file on the next run. +
-------------------------------------------------------------------------------------------------------------------- ## FAQ **Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder. +**A**: Install the application in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous ezFoodie home folder. + +**Q**: Why does my ezFoodie not have any populated sample member data?
+**A**: Delete `ezfoodie.json` and restart the application. Refer to [#24](#24) in *Features* above to find this JSON file. + +**Q**: If I face any unexpected issues and I dont know how to solve it, how can I contact you?
+**A**: Please do not hesitate to drop us an email at `ezfoodie@gmail.com`. -------------------------------------------------------------------------------------------------------------------- ## 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` +Action | Manager Only? (Y/N) | Format, Examples +--------------|---|------------------ +**View Help** | N | *Format:*
`help`
*Example:*
`help` +**Exit Program** | N | *Format:*
`exit`
*Example:*
`exit` +**Login as Manager** | Y | *Format:*
`login `
*Example:*
`login 123456` +**Update a password** | Y | *Format:*
`set -pass/`
*Example:*
`set -pass/123456` +**Logout as Manager** | Y | *Format:*
`logout`
*Example:*
`logout` +**List Members** | N | *Format:*
`list -mem/`
*Example:*
`list -mem/` +**Add New Member** | N | *Format:*
`add -mem/ -n/ -p/ -e/ -a/
`
*Example:*
`add -mem/ -n/John Doe -p/98765432 -e/johndoe@gmail.com -a/112 Amoy Street, 069907, Singapore` +**Delete Member by Member Id** | Y | *Format:*
`del -mem/ -id/`
*Example:*
`del -mem/ -id/00001`
+**Delete Member by Index Number** | Y | *Format:*
`del -mem/ -i/`
*Example:*
`del -mem/ -i/1` +**Find Member by Member Id** | N | *Format:*
`find -mem/ -id/`
*Example:*
`find -mem/ -id/00001` +**Find Members by Name** | N | *Format:*
`find -mem/ -n/`
*Example:*
`find -mem/ -n/John Doe` +**Find Members by Phone** | N | *Format:*
`find -mem/ -p/`
*Example:*
`find -mem/ -p/98765432` +**Find Members by Email** | N | *Format:*
`find -mem/ -e/`
*Example:*
`find -mem/ -e/johndoe@gmail.com` +**Find Members by Registration Date** | N | *Format:*
`find -mem/ -d/`
*Example:*
`find -mem/ -d/2021-01-02` +**Show Member Profile** | N | *Format:*
`show -mem/ -id/`
*Example:*
`show -mem/ -id/00001` +**Edit Member Name by Member Id** | N | *Format:*
`edit -mem/ -id/ -n/`
*Example*
`edit -mem/ -id/00001 -n/John Doe` +**Edit Member Phone by Member ID** | N | *Format*
`edit -mem/ -id/ -p/`
*Example*
`edit -mem/ -id/00001 -p/98765432` +**Edit Member Email by Member Id** | N | *Format:*
`edit -mem/ -id/ -e/`
*Example:*
`edit -mem/ -id/00001 -e/johndoe@gmail.com` +**Edit Member Address by Member ID** | N | *Format:*
`edit -mem/ -id/ -a/
`
*Example:*
`edit -mem/ -id/00001 -a/33 Benoi Crescent, 629979, Singapore` +**Edit Member Name by Index Number** | N | *Format:*
`edit -mem/ -i/ -n/`
*Example:*
`edit -mem/ -i/1 -n/John Doe` +**Edit Member Phone by Index Number** | N | *Format:*
`edit -mem/ -i/ -p/`
*Example:*
`edit -mem/ -i/1 -p/98765432` +**Edit Member Email by Index Number** | N | *Format:*
`edit -mem/ -i/ -e/`
*Example:*
`edit -mem/ -i/1 -e/johndoe@gmail.com` +**Edit Member Address by Index Number** | N | *Format:*
`edit -mem/ -i/ -a/
`
*Example:*
`edit -mem/ -i/1 -a/33 Benoi Crescent, 629979, Singapore` +**Add Transaction for Member** | N | *Format:*
`add -txn/ -id/ -b/`
*Example:*
`add -txn/ -b/200.00 -id/00001` +**Delete Transaction for Member** | Y | *Format:*
`del -txn/ -id/`
*Example:*
`del -txn/ -id/00001000001` +**Edit Transaction** | N | *Format:*
`edit -txn/ -id/ -b/`
*Example:*
`edit -txn/ -id/00001000002 -b/10.00` +**Add Reservation** | N | *Format:*
`add -rs/ -dt/ -rm/ -id/`
*Example:*
`add -rs/ -dt/2021-01-02 00:00 -rm/2 people -id/00001` +**Delete Reservation** | Y | *Format:*
`del -rs/ -id/`
*Example:*
`del -rs/ -id/00001000001` +**Edit Reservation** | N | *Format:*
`edit -rs/ -id/ [-dt/][-rm/]`
*Example:*
`edit -rs/ -id/00001000001 -dt/2021-12-01 13:00 -rm/3 people` +**Sort Members by Credit in Ascending Order** | N | *Format:*
`sort -mem/ -c/ -a/`
*Example:*
`sort -mem/ -c/ -a/` +**Sort Members by Credit in Descending Order** | N | *Format:*
`sort -mem/ -c/ -d/`
*Example:*
`sort -mem/ -c/ -d/` +**Redeem Point from Member by Member Id** | N | *Format:*
`redeem -rd/ -id/`
*Example:*
`redeem -rd/100 -id/00001` +**Redeem Point from Member by Member Index** | N | *Format:*
`redeem -rd/ -i/`
*Example:*
`redeem -rd/100 -i/1` +**Summary** | Y | *Format:*
`summary`
*Example:*
`summary` +**Clear ezFoodie Data** | Y | *Format:*
`clear`
*Example:*
`clear` +**Retrieve command** | N | *Example:*
Press ⬆️ and ⬇️ +**Exiting Summary, Help and Show popup window** | Summary: Y
Help/View: N | *Example:*
Press keyboard `ESC` button diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..8a48efaa299 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "ezFoodie" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2122S1-CS2103T-F12-4/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..5ae3c3753a5 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: "ezFoodie"; font-size: 32px; } } diff --git a/docs/diagrams/AddReservationSequenceDiagram.puml b/docs/diagrams/AddReservationSequenceDiagram.puml new file mode 100644 index 00000000000..21fa8a815fd --- /dev/null +++ b/docs/diagrams/AddReservationSequenceDiagram.puml @@ -0,0 +1,158 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":EzFoodieParser" as EzFoodieParser LOGIC_COLOR +participant ":AddCommandPrefixParser" as AddCommandPrefixParser LOGIC_COLOR +participant ":AddReservationCommandParser" as AddReservationCommandParser LOGIC_COLOR +participant "<>\nParserUtil" as ParserUtil LOGIC_COLOR +participant ":AddReservationCommand" as AddReservationCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "model:Model" as Model MODEL_COLOR +participant "EzFoodie" as EzFoodie MODEL_COLOR +participant "UniqueMemberList" as UniqueMemberList MODEL_COLOR +participant "Member" as Member MODEL_COLOR +participant "FilteredList" as FilteredList MODEL_COLOR +end box + +[-> LogicManager : execute(add -rs/...) +activate LogicManager + +LogicManager -> EzFoodieParser : parseCommand(add -rs/...) +activate EzFoodieParser + +create AddCommandPrefixParser +EzFoodieParser -> AddCommandPrefixParser : AddCommandPrefixParser() +activate AddCommandPrefixParser + +AddCommandPrefixParser --> EzFoodieParser +deactivate AddCommandPrefixParser + +EzFoodieParser -> AddCommandPrefixParser : parse(arguments) +activate AddCommandPrefixParser + +create AddReservationCommandParser +AddCommandPrefixParser -> AddReservationCommandParser : AddReservationCommandParser() +activate AddReservationCommandParser + +AddReservationCommandParser --> AddCommandPrefixParser +deactivate AddReservationCommandParser + +AddCommandPrefixParser --> EzFoodieParser +deactivate AddCommandPrefixParser + +EzFoodieParser -> AddReservationCommandParser : parse(arguments) +activate AddReservationCommandParser + +AddReservationCommandParser -> ParserUtil : parseDateTime(datetime) +activate ParserUtil + +ParserUtil --> AddReservationCommandParser + +AddReservationCommandParser -> ParserUtil : parseRemark(remark) + +ParserUtil --> AddReservationCommandParser + +AddReservationCommandParser -> ParserUtil : parseReservationId(id) + +ParserUtil --> AddReservationCommandParser +deactivate ParserUtil + +create AddReservationCommand +AddReservationCommandParser -> AddReservationCommand : AddReservationCommand(id, datetime, remark) +activate AddReservationCommand + +AddReservationCommand --> AddReservationCommandParser +deactivate AddReservationCommand + +AddReservationCommandParser --> EzFoodieParser +deactivate AddReservationCommandParser + +EzFoodieParser --> LogicManager +deactivate EzFoodieParser + +LogicManager -> AddReservationCommand : execute(model) +activate AddReservationCommand + +AddReservationCommand -> Model : getUpdatedMemberList() +activate Model + +AddReservationCommand -> Member : getReservations() +activate Member + +Member --> AddReservationCommand + +AddReservationCommand -> Member : getName() + +Member --> AddReservationCommand + +AddReservationCommand -> Member : getPhone() + +Member --> AddReservationCommand + +AddReservationCommand -> Member : getEmail() + +Member --> AddReservationCommand + +AddReservationCommand -> Member : getAddress() + +Member --> AddReservationCommand + +AddReservationCommand -> Member : getTimeStamp() + +Member --> AddReservationCommand + +AddReservationCommand -> Member : getCredit() + +Member --> AddReservationCommand + +AddReservationCommand -> Member : getPoints() + +Member --> AddReservationCommand + +AddReservationCommand -> Member : getTransactions() + +Member --> AddReservationCommand + +AddReservationCommand -> Member : getReservations() + +Member --> AddReservationCommand +deactivate Member + +AddReservationCommand -> AddReservationCommand : createEditedMember(memberToEdit, reservationToAdd) + +AddReservationCommand -> Model : getReservationsList() + +Model --> AddReservationCommand + +AddReservationCommand -> Model : setMember(memberToEdit, editedMember) + +Model -> EzFoodie : setMember(target, editedMember) +activate EzFoodie + +EzFoodie -> UniqueMemberList : setMember(target, editedMember) +activate UniqueMemberList + +deactivate UniqueMemberList +deactivate EzFoodie + +AddReservationCommand -> Model : updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS) + + +Model -> FilteredList : setPredicate(predicate) +activate FilteredList + +FilteredList --> Model +deactivate FilteredList + +deactivate Model + +AddReservationCommand --> LogicManager +deactivate AddReservationCommand + +[<-- LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/AddTransactionSequenceDiagram.puml b/docs/diagrams/AddTransactionSequenceDiagram.puml new file mode 100644 index 00000000000..5d5dd901741 --- /dev/null +++ b/docs/diagrams/AddTransactionSequenceDiagram.puml @@ -0,0 +1,153 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":EzFoodieParser" as EzFoodieParser LOGIC_COLOR +participant ":AddCommandPrefixParser" as AddCommandPrefixParser LOGIC_COLOR +participant ":AddTransactionCommandParser" as AddTransactionCommandParser LOGIC_COLOR +participant "<>\nParserUtil" as ParserUtil LOGIC_COLOR +participant ":AddTransactionCommand" as AddTransactionCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "model:Model" as Model MODEL_COLOR +participant "ezFoodie:EzFoodie" as EzFoodie MODEL_COLOR +participant "uniqueMemberList:UniqueMemberList" as UniqueMemberList MODEL_COLOR +participant "filteredList:FilteredList" as FilteredList MODEL_COLOR +participant "transaction:Transaction" as Transaction MODEL_COLOR +participant "memberToEdit:Member" as Member MODEL_COLOR +end box + +[-> LogicManager : execute(add -txn/...) +activate LogicManager + +LogicManager -> EzFoodieParser : parseCommand(add -txn/...) +activate EzFoodieParser + +create AddCommandPrefixParser +EzFoodieParser -> AddCommandPrefixParser : AddCommandPrefixParser() +activate AddCommandPrefixParser + +AddCommandPrefixParser --> EzFoodieParser +deactivate AddCommandPrefixParser + +EzFoodieParser -> AddCommandPrefixParser : parse(arguments) +activate AddCommandPrefixParser + +create AddTransactionCommandParser +AddCommandPrefixParser -> AddTransactionCommandParser : AddTransactionCommandParser() +activate AddTransactionCommandParser + +AddTransactionCommandParser --> AddCommandPrefixParser +deactivate AddTransactionCommandParser + +AddCommandPrefixParser --> EzFoodieParser +deactivate AddCommandPrefixParser + +EzFoodieParser -> AddTransactionCommandParser : parse(arguments) +activate AddTransactionCommandParser + +AddTransactionCommandParser -> ParserUtil : parseBilling(billing) +activate ParserUtil + +ParserUtil --> AddTransactionCommandParser + +AddTransactionCommandParser -> ParserUtil : parseTimestamp(timestamp) +ParserUtil --> AddTransactionCommandParser + +AddTransactionCommandParser -> ParserUtil : parseMemberId(memberId) +ParserUtil --> AddTransactionCommandParser + +AddTransactionCommandParser -> ParserUtil : parseTransactionId(transactionId) +ParserUtil --> AddTransactionCommandParser + +deactivate ParserUtil + +AddTransactionCommandParser -> Transaction : Transaction(transactionId, timestamp, billing) +activate Transaction + +Transaction -> AddTransactionCommandParser +deactivate Transaction + +create AddTransactionCommand +AddTransactionCommandParser -> AddTransactionCommand : AddTransactionCommand(transaction, memberId) +activate AddTransactionCommand + +AddTransactionCommand --> AddTransactionCommandParser +deactivate AddTransactionCommand + +AddTransactionCommandParser --> EzFoodieParser +deactivate AddTransactionCommandParser + +EzFoodieParser --> LogicManager +deactivate EzFoodieParser + +LogicManager -> AddTransactionCommand : execute(model) +activate AddTransactionCommand + +AddTransactionCommand -> Model : getUpdatedMemberList() +activate Model + +Model --> AddTransactionCommand + + +AddTransactionCommand -> AddTransactionCommand : createEditedMember(memberToEdit, transactionToAdd) + + +AddTransactionCommand -> Member : getName() +activate Member + +Member -> AddTransactionCommand + +AddTransactionCommand -> Member : getPhone() +Member -> AddTransactionCommand + +AddTransactionCommand -> Member : getEmail() +Member -> AddTransactionCommand + +AddTransactionCommand -> Member : getAddress() +Member -> AddTransactionCommand + +AddTransactionCommand -> Member : getTimestamp() +Member -> AddTransactionCommand + +AddTransactionCommand -> Member : getTransactions() +Member -> AddTransactionCommand + +AddTransactionCommand -> Member : getReservations() +Member -> AddTransactionCommand + +AddTransactionCommand -> Member : getTags() +Member -> AddTransactionCommand + +deactivate Member + +AddTransactionCommand -> Model : setMember(memberToEdit, editedMember) + +Model -> EzFoodie : setMember(target, editedMember) +activate EzFoodie + +EzFoodie -> UniqueMemberList : setMember(target, editedMember) +activate UniqueMemberList + +deactivate UniqueMemberList +deactivate EzFoodie + +AddTransactionCommand -> Model : updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS) + + +Model -> FilteredList : setPredicate(predicate) +activate FilteredList + +FilteredList --> Model +deactivate FilteredList + +deactivate Model + +AddTransactionCommand --> LogicManager +deactivate AddTransactionCommand + +[<-- LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index ef81d18c337..3f490b4d119 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -7,19 +7,19 @@ 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 : "del -mem/ -i/1" activate ui UI_COLOR -ui -[UI_COLOR]> logic : execute("delete 1") +ui -[UI_COLOR]> logic : execute("del -mem/ -i/1") activate logic LOGIC_COLOR -logic -[LOGIC_COLOR]> model : deletePerson(p) +logic -[LOGIC_COLOR]> model : deleteMember(member) activate model MODEL_COLOR model -[MODEL_COLOR]-> logic deactivate model -logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook) +logic -[LOGIC_COLOR]> storage : saveEzFoodie(ezFoodie) activate storage STORAGE_COLOR storage -[STORAGE_COLOR]> storage : Save to file diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml index 5731f9cbaa1..9264d8df940 100644 --- a/docs/diagrams/BetterModelClassDiagram.puml +++ b/docs/diagrams/BetterModelClassDiagram.puml @@ -4,18 +4,27 @@ skinparam arrowThickness 1.1 skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR -AddressBook *-right-> "1" UniquePersonList -AddressBook *-right-> "1" UniqueTagList -UniqueTagList -[hidden]down- UniquePersonList -UniqueTagList -[hidden]down- UniquePersonList +EzFoodie *-right-> "1" UniqueMemberList +EzFoodie *-right-> "1" UniqueReservationList +UniqueReservationList -[hidden]down- UniqueMemberList +UniqueReservationList -[hidden]down- UniqueMemberList -UniqueTagList *-right-> "*" Tag -UniquePersonList -right-> Person +UniqueReservationList *-right-> "*" Reservation +UniqueMemberList -right-> Member -Person -up-> "*" Tag +Member -up-> "*" Reservation -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address +Member *--> "1" MemberId +Member *--> "1" Name +Member *--> "1" Phone +Member *--> "1" Email +Member *--> "1" Credit +Member *--> "1" Point +Member *--> "1" Tier +Member *--> "1" RegistrationTimestamp +Member *--> "1" Address +Member *--> "*" Transaction + +Reservation *-up-> "1" Remark +Reservation *-up-> "1" ReservationDateTime @enduml diff --git a/docs/diagrams/CommitActivityDiagram.puml b/docs/diagrams/CommitActivityDiagram.puml index 6a6b23a006f..c96b601e283 100644 --- a/docs/diagrams/CommitActivityDiagram.puml +++ b/docs/diagrams/CommitActivityDiagram.puml @@ -5,10 +5,10 @@ start 'Since the beta syntax does not support placing the condition outside the 'diamond we place it as the true branch instead. -if () then ([command commits AddressBook]) +if () then ([command commits EzFoodie]) :Purge redundant states; - :Save AddressBook to - addressBookStateList; + :Save EzFoodie to + ezFoodieStateList; else ([else]) endif stop diff --git a/docs/diagrams/DeleteReservationSequenceDiagram.puml b/docs/diagrams/DeleteReservationSequenceDiagram.puml new file mode 100644 index 00000000000..ed30ab59fdd --- /dev/null +++ b/docs/diagrams/DeleteReservationSequenceDiagram.puml @@ -0,0 +1,148 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":EzFoodieParser" as EzFoodieParser LOGIC_COLOR +participant ":DeleteCommandPrefixParser" as DeleteCommandPrefixParser LOGIC_COLOR +participant ":DeleteReservationCommandParser" as DeleteReservationCommandParser LOGIC_COLOR +participant "<>\nParserUtil" as ParserUtil LOGIC_COLOR +participant ":DeleteReservationCommand" as DeleteReservationCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "model:Model" as Model MODEL_COLOR +participant "EzFoodie" as EzFoodie MODEL_COLOR +participant "UniqueMemberList" as UniqueMemberList MODEL_COLOR +participant "Member" as Member MODEL_COLOR +participant "FilteredList" as FilteredList MODEL_COLOR +end box + +[-> LogicManager : execute(add -rs/...) +activate LogicManager + +LogicManager -> EzFoodieParser : parseCommand(add -rs/...) +activate EzFoodieParser + +create DeleteCommandPrefixParser +EzFoodieParser -> DeleteCommandPrefixParser : DeleteCommandPrefixParser() +activate DeleteCommandPrefixParser + +DeleteCommandPrefixParser --> EzFoodieParser +deactivate DeleteCommandPrefixParser + +EzFoodieParser -> DeleteCommandPrefixParser : parse(arguments) +activate DeleteCommandPrefixParser + +create DeleteReservationCommandParser +DeleteCommandPrefixParser -> DeleteReservationCommandParser : DeleteReservationCommandParser() +activate DeleteReservationCommandParser + +DeleteReservationCommandParser --> DeleteCommandPrefixParser +deactivate DeleteReservationCommandParser + +DeleteCommandPrefixParser --> EzFoodieParser +deactivate DeleteCommandPrefixParser + +EzFoodieParser -> DeleteReservationCommandParser : parse(arguments) +activate DeleteReservationCommandParser + +DeleteReservationCommandParser -> ParserUtil : parseMemberId(memberId) +activate ParserUtil + +ParserUtil --> DeleteReservationCommandParser + +DeleteReservationCommandParser -> ParserUtil : parseReservationId(reservationId) + +ParserUtil --> DeleteReservationCommandParser +deactivate ParserUtil + +create DeleteReservationCommand +DeleteReservationCommandParser -> DeleteReservationCommand : DeleteReservationCommand(memberId, (reservationId)) +activate DeleteReservationCommand + +DeleteReservationCommand --> DeleteReservationCommandParser +deactivate DeleteReservationCommand + +DeleteReservationCommandParser --> EzFoodieParser +deactivate DeleteReservationCommandParser + +EzFoodieParser --> LogicManager +deactivate EzFoodieParser + +LogicManager -> DeleteReservationCommand : execute(model) +activate DeleteReservationCommand + +DeleteReservationCommand -> Model : getUpdatedMemberList() +activate Model + +DeleteReservationCommand -> Member : getReservations() +activate Member + +Model --> DeleteReservationCommand + +DeleteReservationCommand -> Member : getName() + +Member --> DeleteReservationCommand + +DeleteReservationCommand -> Member : getPhone() + +Member --> DeleteReservationCommand + +DeleteReservationCommand -> Member : getEmail() + +Member --> DeleteReservationCommand + +DeleteReservationCommand -> Member : getAddress() + +Member --> DeleteReservationCommand + +DeleteReservationCommand -> Member : getTimeStamp() + +Member --> DeleteReservationCommand + +DeleteReservationCommand -> Member : getCredit() + +Member --> DeleteReservationCommand + +DeleteReservationCommand -> Member : getPoints() + +Member --> DeleteReservationCommand + +DeleteReservationCommand -> Member : getTransactions() + +Member --> DeleteReservationCommand + +DeleteReservationCommand -> Member : getReservations() + +Member --> DeleteReservationCommand +deactivate Member + +DeleteReservationCommand -> Model : setMember(memberToEdit, editedMember) + +Model -> EzFoodie : setMember(target, editedMember) +activate EzFoodie + +EzFoodie -> UniqueMemberList : setMember(target, editedMember) +activate UniqueMemberList + +deactivate UniqueMemberList +deactivate EzFoodie + +DeleteReservationCommand -> Model : updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS) + + +Model -> FilteredList : setPredicate(predicate) +activate FilteredList + +FilteredList --> Model +deactivate FilteredList + +deactivate Model + +DeleteReservationCommand --> LogicManager +deactivate DeleteReservationCommand + +[<-- LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 1dc2311b245..f6be423b059 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -3,9 +3,11 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR -participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR -participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR +participant ":EzFoodieParser" as EzFoodieParser LOGIC_COLOR + +participant ":DeleteCommandPrefixParser" as DeleteCommandPrefixParser LOGIC_COLOR +participant ":DeleteMemberCommandParser" as DeleteMemberCommandParser LOGIC_COLOR +participant "command:DeleteMemberCommand" as DeleteMemberCommand LOGIC_COLOR participant ":CommandResult" as CommandResult LOGIC_COLOR end box @@ -13,56 +15,84 @@ box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR end box -[-> LogicManager : execute("delete 1") +[-> LogicManager : execute("del -mem/ -i/1") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") -activate AddressBookParser +LogicManager -> EzFoodieParser : parseCommand("del -mem/ -i/1") +activate EzFoodieParser + +create DeleteCommandPrefixParser +EzFoodieParser -> DeleteCommandPrefixParser : DeleteCommandPrefixParser() +activate DeleteCommandPrefixParser + +DeleteCommandPrefixParser --> EzFoodieParser +deactivate DeleteCommandPrefixParser + +EzFoodieParser -> DeleteCommandPrefixParser : parse(" -mem/ -i/1") +activate DeleteCommandPrefixParser + +create DeleteMemberCommandParser +DeleteCommandPrefixParser -> DeleteMemberCommandParser : DeleteMemberCommandParser() +activate DeleteMemberCommandParser + +DeleteMemberCommandParser --> DeleteCommandPrefixParser +deactivate DeleteMemberCommandParser + +DeleteCommandPrefixParser --> EzFoodieParser +deactivate DeleteCommandPrefixParser -create DeleteCommandParser -AddressBookParser -> DeleteCommandParser -activate DeleteCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +DeleteCommandPrefixParser -[hidden]-> EzFoodieParser +destroy DeleteCommandPrefixParser -DeleteCommandParser --> AddressBookParser -deactivate DeleteCommandParser +EzFoodieParser -> DeleteMemberCommandParser : parse(" -mem/ -i/1") +activate DeleteMemberCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") -activate DeleteCommandParser +create DeleteMemberCommand +DeleteMemberCommandParser -> DeleteMemberCommand : DeleteMemberCommand(1) +activate DeleteMemberCommand -create DeleteCommand -DeleteCommandParser -> DeleteCommand -activate DeleteCommand +DeleteMemberCommand --> DeleteMemberCommandParser : command +deactivate DeleteMemberCommand -DeleteCommand --> DeleteCommandParser : d -deactivate DeleteCommand +DeleteMemberCommandParser --> EzFoodieParser : command +deactivate DeleteMemberCommandParser -DeleteCommandParser --> AddressBookParser : d -deactivate DeleteCommandParser 'Hidden arrow to position the destroy marker below the end of the activation bar. -DeleteCommandParser -[hidden]-> AddressBookParser -destroy DeleteCommandParser +DeleteMemberCommandParser -[hidden]-> EzFoodieParser +destroy DeleteMemberCommandParser -AddressBookParser --> LogicManager : d -deactivate AddressBookParser +EzFoodieParser --> LogicManager : command +deactivate EzFoodieParser -LogicManager -> DeleteCommand : execute() -activate DeleteCommand +LogicManager -> DeleteMemberCommand : execute(model) +activate DeleteMemberCommand -DeleteCommand -> Model : deletePerson(1) +DeleteMemberCommand -> Model : getFilteredPatientList() activate Model -Model --> DeleteCommand +Model --> DeleteMemberCommand : lastShownList +deactivate Model + +DeleteMemberCommand -> Model : deleteMember(memberToDelete) +activate Model + +Model --> DeleteMemberCommand deactivate Model create CommandResult -DeleteCommand -> CommandResult +DeleteMemberCommand -> CommandResult : CommandResult(message) activate CommandResult -CommandResult --> DeleteCommand +CommandResult --> DeleteMemberCommand deactivate CommandResult -DeleteCommand --> LogicManager : result -deactivate DeleteCommand +DeleteMemberCommand --> LogicManager : result +deactivate DeleteMemberCommand + +'Hidden arrow to position the destroy marker below the end of the activation bar. +DeleteMemberCommand -[hidden]-> LogicManager +destroy DeleteMemberCommand [<--LogicManager deactivate LogicManager diff --git a/docs/diagrams/FindActivityDiagram.puml b/docs/diagrams/FindActivityDiagram.puml new file mode 100644 index 00000000000..2f6896743f5 --- /dev/null +++ b/docs/diagrams/FindActivityDiagram.puml @@ -0,0 +1,19 @@ +@startuml +start +:User executes command start with "find"; + +:Check command; + +if () then ([user command is valid]) + :Parse arguments from command; + :Extract keywords from arguments; + :Create predicate with keywords; + :Filter members based on predicate; + :List filtered members; + :Show success message to user; +else ([user command is invalid]) + :Show error message to user; +endif + +stop +@enduml diff --git a/docs/diagrams/FindSequenceDiagram.puml b/docs/diagrams/FindSequenceDiagram.puml new file mode 100644 index 00000000000..2ca7948ecb5 --- /dev/null +++ b/docs/diagrams/FindSequenceDiagram.puml @@ -0,0 +1,81 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":EzFoodieParser" as EzFoodieParser LOGIC_COLOR +participant ":FindCommandParser" as FindCommandParser LOGIC_COLOR +participant "predicate:IdContainsKeywordsPredicate" as IdContainsKeywordsPredicate LOGIC_COLOR +participant "command:FindCommand" as FindCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "model:Model" as Model MODEL_COLOR +participant ":FilteredList" as FilteredList MODEL_COLOR +end box +[-> LogicManager : execute("find -mem/ -id/00001") +activate LogicManager + +LogicManager -> EzFoodieParser : parseCommand("find -mem/ -id/00001") +activate EzFoodieParser + +create FindCommandParser +EzFoodieParser -> FindCommandParser : FindCommandParser() +activate FindCommandParser + +FindCommandParser --> EzFoodieParser +deactivate FindCommandParser + +EzFoodieParser -> FindCommandParser : parse(" -mem/ -id/00001") +activate FindCommandParser + +create IdContainsKeywordsPredicate +FindCommandParser -> IdContainsKeywordsPredicate : IdContainsKeywordsPredicate([00001]) +activate IdContainsKeywordsPredicate + +IdContainsKeywordsPredicate --> FindCommandParser +deactivate IdContainsKeywordsPredicate + +create FindCommand +FindCommandParser -> FindCommand : FindCommand(predicate) +activate FindCommand + +FindCommand --> FindCommandParser +deactivate FindCommand + +FindCommandParser --> EzFoodieParser +deactivate FindCommandParser + +FindCommandParser -[hidden]-> LogicManager : return +destroy FindCommandParser + +EzFoodieParser --> LogicManager : command +deactivate EzFoodieParser + +LogicManager -> FindCommand : execute(model) +activate FindCommand + +FindCommand -> Model : updateFilteredMemberList(predicate) +activate Model + +Model -> FilteredList : setPredicate(predicate) +activate FilteredList + +FilteredList --> Model +deactivate FilteredList + +Model --> FindCommand +deactivate Model + +FindCommand --> LogicManager : result +deactivate FindCommand + +FindCommand -[hidden]-> LogicManager : return +destroy FindCommand + +IdContainsKeywordsPredicate -[hidden]-> LogicManager : return +destroy IdContainsKeywordsPredicate + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index 6d14b17b361..ae65f445e33 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -6,8 +6,9 @@ skinparam classBackgroundColor LOGIC_COLOR package Logic { -Class AddressBookParser -Class XYZCommand +Class EzFoodieParser +Class UVWXYZCommand +Class UVWCommand Class CommandResult Class "{abstract}\nCommand" as Command @@ -27,10 +28,12 @@ Class HiddenOutside #FFFFFF HiddenOutside ..> Logic LogicManager .right.|> Logic -LogicManager -right->"1" AddressBookParser -AddressBookParser ..> XYZCommand : creates > +LogicManager -right->"1" EzFoodieParser +EzFoodieParser ..> UVWXYZCommand : creates > +EzFoodieParser ..> UVWCommand : creates > -XYZCommand -up-|> Command +UVWXYZCommand -up-|> Command +UVWCommand -up-|> Command LogicManager .left.> Command : executes > LogicManager --> Model @@ -38,7 +41,8 @@ LogicManager --> Storage Storage --[hidden] Model Command .[hidden]up.> Storage Command .right.> Model -note right of XYZCommand: XYZCommand = AddCommand, \nFindCommand, etc +note right of UVWXYZCommand: UVW = Add, Edit, etc \nXYZ = Member, Transaction, etc \nUVWXYZCommand = AddMemberCommand, \nEditTransactionCommand, etc +note right of UVWCommand: UVWCommand = FindCommand, \nShowCommand, etc Logic ..> CommandResult LogicManager .down.> CommandResult diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 1122257bd9a..601cc866435 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -5,50 +5,83 @@ skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR Package Model <>{ -Interface ReadOnlyAddressBook <> +Interface ReadOnlyEzFoodie <> Interface ReadOnlyUserPrefs <> Interface Model <> -Class AddressBook -Class ReadOnlyAddressBook +Class EzFoodie +Class ReadOnlyEzFoodie Class Model Class ModelManager Class UserPrefs Class ReadOnlyUserPrefs - -Class UniquePersonList -Class Person +Class UniqueMemberList +Class Member +Class MemberId Class Address Class Email Class Name Class Phone -Class Tag +Class Credit +Class Point +Class RegistrationTimestamp +Class Tier +Class Transaction +Class Reservation + +Class TransactionId +Class Billing +Class TransactionTimestamp + +Class ReservationId +Class Remark +Class ReservationDateTime } Class HiddenOutside #FFFFFF HiddenOutside ..> Model -AddressBook .up.|> ReadOnlyAddressBook +EzFoodie .up.|> ReadOnlyEzFoodie ModelManager .up.|> Model Model .right.> ReadOnlyUserPrefs -Model .left.> ReadOnlyAddressBook -ModelManager -left-> "1" AddressBook +Model .left.> ReadOnlyEzFoodie +ModelManager -left-> "1" EzFoodie ModelManager -right-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs -AddressBook *--> "1" UniquePersonList -UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag +EzFoodie *--> "1" UniqueMemberList +UniqueMemberList --> "~* all" Member +Member *--> "1" MemberId +Member *--> "1" Name +Member *--> "1" Phone +Member *--> "1" Email +Member *--> "1" Credit +Member *--> "1" Point +Member *--> "1" Tier +Member *--> "1" RegistrationTimestamp +Member *--> "1" Address +Member *--> "*" Transaction +Member *--> "*" Reservation + +Transaction *--> "1" TransactionId +Transaction *--> "1" Billing +Transaction *--> "1" TransactionTimestamp + +Reservation *--> "1" ReservationId +Reservation *--> "1" Remark +Reservation *--> "1" ReservationDateTime +MemberId -[hidden]right-> Name Name -[hidden]right-> Phone -Phone -[hidden]right-> Address -Address -[hidden]right-> Email +Phone -[hidden]right-> Email +Email -[hidden]right-> Credit +Credit -[hidden]right-> Point +Point -[hidden]right-> Tier +Tier -[hidden]right-> RegistrationTimestamp +Address -[hidden]right-> Transaction +Transaction -[hidden]right-> Reservation -ModelManager -->"~* filtered" Person +ModelManager -->"~* filtered" Member @enduml diff --git a/docs/diagrams/ParserClassesCase1.puml b/docs/diagrams/ParserClassesCase1.puml new file mode 100644 index 00000000000..43bf0224ef7 --- /dev/null +++ b/docs/diagrams/ParserClassesCase1.puml @@ -0,0 +1,40 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor LOGIC_COLOR_T4 +skinparam classBackgroundColor LOGIC_COLOR + +Class "{abstract}\nCommand" as Command +Class UVWXYZCommand + +package "Parser classes"{ +Interface Parser <> +Class EzFoodieParser +Class UVWCommandPrefixParser +Class UVWXYZCommandParser +Class CliSyntax +Class ParserUtil +Class ArgumentMultimap +Class ArgumentTokenizer +Class Prefix +} + +Class HiddenOutside #FFFFFF +HiddenOutside ..> EzFoodieParser + +EzFoodieParser .down.> UVWCommandPrefixParser: creates > +UVWCommandPrefixParser .down.> UVWXYZCommandParser: creates > + +UVWXYZCommandParser ..> UVWXYZCommand : creates > +EzFoodieParser ..> Command : returns > +UVWXYZCommandParser .up.|> Parser +UVWXYZCommandParser ..> ArgumentMultimap +UVWXYZCommandParser ..> ArgumentTokenizer +ArgumentTokenizer .left.> ArgumentMultimap +UVWXYZCommandParser ..> CliSyntax +CliSyntax ..> Prefix +UVWXYZCommandParser ..> ParserUtil +ParserUtil .down.> Prefix +ArgumentTokenizer .down.> Prefix +UVWXYZCommand -up-|> Command +@enduml diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClassesCase2.puml similarity index 57% rename from docs/diagrams/ParserClasses.puml rename to docs/diagrams/ParserClassesCase2.puml index 6ba585cba01..0bda777dadf 100644 --- a/docs/diagrams/ParserClasses.puml +++ b/docs/diagrams/ParserClassesCase2.puml @@ -9,8 +9,8 @@ Class XYZCommand package "Parser classes"{ Interface Parser <> -Class AddressBookParser -Class XYZCommandParser +Class EzFoodieParser +Class UVWCommandParser Class CliSyntax Class ParserUtil Class ArgumentMultimap @@ -19,19 +19,19 @@ Class Prefix } Class HiddenOutside #FFFFFF -HiddenOutside ..> AddressBookParser +HiddenOutside ..> EzFoodieParser -AddressBookParser .down.> XYZCommandParser: creates > +EzFoodieParser .down.> UVWCommandParser: creates > -XYZCommandParser ..> XYZCommand : creates > -AddressBookParser ..> Command : returns > -XYZCommandParser .up.|> Parser -XYZCommandParser ..> ArgumentMultimap -XYZCommandParser ..> ArgumentTokenizer +UVWCommandParser ..> XYZCommand : creates > +EzFoodieParser ..> Command : returns > +UVWCommandParser .up.|> Parser +UVWCommandParser ..> ArgumentMultimap +UVWCommandParser ..> ArgumentTokenizer ArgumentTokenizer .left.> ArgumentMultimap -XYZCommandParser ..> CliSyntax +UVWCommandParser ..> CliSyntax CliSyntax ..> Prefix -XYZCommandParser ..> ParserUtil +UVWCommandParser ..> ParserUtil ParserUtil .down.> Prefix ArgumentTokenizer .down.> Prefix XYZCommand -up-|> Command diff --git a/docs/diagrams/SortActivityDiagram.puml b/docs/diagrams/SortActivityDiagram.puml new file mode 100644 index 00000000000..17c70fd0b56 --- /dev/null +++ b/docs/diagrams/SortActivityDiagram.puml @@ -0,0 +1,19 @@ +@startuml +start +:User executes command start with "sort"; + +:Check command; + +if () then ([user command is valid]) + :Parse arguments from command; + :Extract sort order from arguments; + :Create comparator with sort order; + :Sort members based on comparator; + :List sorted members; + :Show success message to user; +else ([user command is invalid]) + :Show error message to user; +endif + +stop +@enduml diff --git a/docs/diagrams/SortSequenceDiagram.puml b/docs/diagrams/SortSequenceDiagram.puml new file mode 100644 index 00000000000..9576dc588a9 --- /dev/null +++ b/docs/diagrams/SortSequenceDiagram.puml @@ -0,0 +1,81 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":EzFoodieParser" as EzFoodieParser LOGIC_COLOR +participant ":SortCommandParser" as SortCommandParser LOGIC_COLOR +participant "comparator:CreditSortComparator" as CreditSortComparator LOGIC_COLOR +participant "command:SortCommand" as SortCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "model:Model" as Model MODEL_COLOR +participant ":SortedList" as SortedList MODEL_COLOR +end box +[-> LogicManager : execute("sort -mem/ -c/ -a/") +activate LogicManager + +LogicManager -> EzFoodieParser : parseCommand("sort -mem/ -c/ -a/") +activate EzFoodieParser + +create SortCommandParser +EzFoodieParser -> SortCommandParser : SortCommandParser() +activate SortCommandParser + +SortCommandParser --> EzFoodieParser +deactivate SortCommandParser + +EzFoodieParser -> SortCommandParser : parse(" -mem/ -c/ -a/") +activate SortCommandParser + +create CreditSortComparator +SortCommandParser -> CreditSortComparator : CreditSortComparator(SortStatus.ASC) +activate CreditSortComparator + +CreditSortComparator --> SortCommandParser +deactivate CreditSortComparator + +create SortCommand +SortCommandParser -> SortCommand : SortCommand(comparator) +activate SortCommand + +SortCommand --> SortCommandParser +deactivate SortCommand + +SortCommandParser --> EzFoodieParser +deactivate SortCommandParser + +SortCommandParser -[hidden]-> LogicManager : return +destroy SortCommandParser + +EzFoodieParser --> LogicManager : command +deactivate EzFoodieParser + +LogicManager -> SortCommand : execute(model) +activate SortCommand + +SortCommand -> Model : updateSortedMemberList(comparator) +activate Model + +Model -> SortedList : setComparator(comparator) +activate SortedList + +SortedList --> Model +deactivate SortedList + +Model --> SortCommand +deactivate Model + +SortCommand --> LogicManager : result +deactivate SortCommand + +SortCommand -[hidden]-> LogicManager : return +destroy SortCommand + +CreditSortComparator -[hidden]-> LogicManager : return +destroy CreditSortComparator + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 85ac3ea2dee..63543d08ffe 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -6,6 +6,11 @@ skinparam classBackgroundColor STORAGE_COLOR package Storage{ +package "Account Storage" #F4F6F6{ +Interface AccountStorage <> +Class JsonAccountStorage +} + package "UserPrefs Storage" #F4F6F6{ Interface UserPrefsStorage <> Class JsonUserPrefsStorage @@ -14,12 +19,13 @@ Class JsonUserPrefsStorage Interface Storage <> Class StorageManager -package "AddressBook Storage" #F4F6F6{ -Interface AddressBookStorage <> -Class JsonAddressBookStorage -Class JsonSerializableAddressBook -Class JsonAdaptedPerson -Class JsonAdaptedTag +package "EzFoodie Storage" #F4F6F6{ +Interface EzFoodieStorage <> +Class JsonEzFoodieStorage +Class JsonSerializableEzFoodie +Class JsonAdaptedMember +Class JsonAdaptedTransaction +Class JsonAdaptedReservation } } @@ -28,16 +34,22 @@ Class HiddenOutside #FFFFFF HiddenOutside ..> Storage StorageManager .up.|> Storage + +StorageManager -up-> "1" AccountStorage StorageManager -up-> "1" UserPrefsStorage -StorageManager -up-> "1" AddressBookStorage +StorageManager -up-> "1" EzFoodieStorage +Storage -left-|> AccountStorage Storage -left-|> UserPrefsStorage -Storage -right-|> AddressBookStorage +Storage -right-|> EzFoodieStorage +JsonAccountStorage .up.|> AccountStorage JsonUserPrefsStorage .up.|> UserPrefsStorage -JsonAddressBookStorage .up.|> AddressBookStorage -JsonAddressBookStorage ..> JsonSerializableAddressBook -JsonSerializableAddressBook --> "*" JsonAdaptedPerson -JsonAdaptedPerson --> "*" JsonAdaptedTag +JsonEzFoodieStorage .up.|> EzFoodieStorage + +JsonEzFoodieStorage ..> JsonSerializableEzFoodie +JsonSerializableEzFoodie --> "*" JsonAdaptedMember +JsonAdaptedMember --> "*" JsonAdaptedTransaction +JsonAdaptedMember --> "*" JsonAdaptedReservation @enduml diff --git a/docs/diagrams/SummarySequenceDiagram.puml b/docs/diagrams/SummarySequenceDiagram.puml new file mode 100644 index 00000000000..2ed6648066f --- /dev/null +++ b/docs/diagrams/SummarySequenceDiagram.puml @@ -0,0 +1,33 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":EzFoodieParser" as EzFoodieParser LOGIC_COLOR +participant ":SummaryCommand" as SummaryCommand LOGIC_COLOR +end box + + +[-> LogicManager : execute('summary') +activate LogicManager + +LogicManager -> EzFoodieParser : parseCommand('summary') +activate EzFoodieParser + +create SummaryCommand +EzFoodieParser -> SummaryCommand : SummaryCommand() +activate SummaryCommand + +SummaryCommand -> EzFoodieParser + +EzFoodieParser --> LogicManager +deactivate EzFoodieParser + +LogicManager -> SummaryCommand : execute(model) + +SummaryCommand --> LogicManager +deactivate SummaryCommand + +[<-- LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/UiClassDiagramHelpWindow.puml b/docs/diagrams/UiClassDiagramHelpWindow.puml new file mode 100644 index 00000000000..aad20a6ce6f --- /dev/null +++ b/docs/diagrams/UiClassDiagramHelpWindow.puml @@ -0,0 +1,19 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor UI_COLOR_T4 +skinparam classBackgroundColor UI_COLOR + +package UI <>{ +Class "{abstract}\nUiPart" as UiPart +Class HelpWindow +Class HelpBox +} + +HelpWindow *-down-> "1" HelpBox + +HelpWindow --|> UiPart +HelpBox --|> UiPart + +HelpWindow -[hidden]-|> UiPart +@enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagramMainWindow.puml similarity index 62% rename from docs/diagrams/UiClassDiagram.puml rename to docs/diagrams/UiClassDiagramMainWindow.puml index ecae4876432..dbea44fb740 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagramMainWindow.puml @@ -11,14 +11,11 @@ Class UiManager Class MainWindow Class HelpWindow Class ResultDisplay -Class PersonListPanel -Class PersonCard +Class MemberListPanel Class StatusBarFooter Class CommandBox -} - -package Model <> { -Class HiddenModel #FFFFFF +Class MemberViewWindow +Class SummaryWindow } package Logic <> { @@ -32,27 +29,31 @@ UiManager .left.|> Ui UiManager -down-> "1" MainWindow MainWindow *-down-> "1" CommandBox MainWindow *-down-> "1" ResultDisplay -MainWindow *-down-> "1" PersonListPanel +MainWindow *-down-> "1" MemberListPanel MainWindow *-down-> "1" StatusBarFooter MainWindow --> "0..1" HelpWindow +MainWindow --> "0..1" MemberViewWindow +MainWindow --> "0..1" SummaryWindow -PersonListPanel -down-> "*" PersonCard - -MainWindow -left-|> UiPart +MainWindow -right-|> UiPart ResultDisplay --|> UiPart CommandBox --|> UiPart -PersonListPanel --|> UiPart -PersonCard --|> UiPart +MemberListPanel --|> UiPart StatusBarFooter --|> UiPart HelpWindow --|> UiPart +MemberViewWindow --|> UiPart +SummaryWindow --|> UiPart -PersonCard ..> Model -UiManager -right-> Logic +UiManager -left-> Logic MainWindow -left-> Logic +MemberViewWindow -left-> Logic +SummaryWindow -left-> Logic -PersonListPanel -[hidden]left- HelpWindow -HelpWindow -[hidden]left- CommandBox +MemberViewWindow -[hidden]left- SummaryWindow +SummaryWindow -[hidden]left- HelpWindow +HelpWindow -[hidden]left- MemberListPanel +MemberListPanel -[hidden]left- CommandBox CommandBox -[hidden]left- ResultDisplay ResultDisplay -[hidden]left- StatusBarFooter diff --git a/docs/diagrams/UiClassDiagramMemberListPanel.puml b/docs/diagrams/UiClassDiagramMemberListPanel.puml new file mode 100644 index 00000000000..fa1adc01087 --- /dev/null +++ b/docs/diagrams/UiClassDiagramMemberListPanel.puml @@ -0,0 +1,25 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor UI_COLOR_T4 +skinparam classBackgroundColor UI_COLOR + +package UI <>{ +Class "{abstract}\nUiPart" as UiPart +Class MemberListPanel +Class MemberCard +} + +package Model <> { +Class HiddenModel #FFFFFF +} + +MemberListPanel *-down-> "*" MemberCard + +MemberListPanel --|> UiPart +MemberCard --|> UiPart + +MemberCard ..> Model + +MemberListPanel -[hidden]-|> UiPart +@enduml diff --git a/docs/diagrams/UiClassDiagramMemberViewWindow.puml b/docs/diagrams/UiClassDiagramMemberViewWindow.puml new file mode 100644 index 00000000000..61e19846cd6 --- /dev/null +++ b/docs/diagrams/UiClassDiagramMemberViewWindow.puml @@ -0,0 +1,29 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor UI_COLOR_T4 +skinparam classBackgroundColor UI_COLOR + +package UI <>{ +Class "{abstract}\nUiPart" as UiPart +Class MemberViewWindow +Class MemberDetailsListPanel +Class MemberDetailsCard +} + +package Model <> { +Class HiddenModel #FFFFFF +} + +MemberViewWindow *-down-> "1" MemberDetailsListPanel + +MemberDetailsListPanel -down-> "*" MemberDetailsCard + +MemberViewWindow --|> UiPart +MemberDetailsListPanel --|> UiPart +MemberDetailsCard --|> UiPart + +MemberDetailsCard ..> Model + +MemberViewWindow -[hidden]-|> UiPart +@enduml diff --git a/docs/diagrams/UiClassDiagramSummaryWindow.puml b/docs/diagrams/UiClassDiagramSummaryWindow.puml new file mode 100644 index 00000000000..ad2d8443877 --- /dev/null +++ b/docs/diagrams/UiClassDiagramSummaryWindow.puml @@ -0,0 +1,25 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor UI_COLOR_T4 +skinparam classBackgroundColor UI_COLOR + +package UI <>{ +Class "{abstract}\nUiPart" as UiPart +Class SummaryWindow +Class SummaryBox +} + +package Model <> { +Class HiddenModel #FFFFFF +} + +SummaryWindow *-down-> "1" SummaryBox + +SummaryWindow --|> UiPart +SummaryBox --|> UiPart + +SummaryBox ..> Model + +SummaryWindow -[hidden]-|> UiPart +@enduml diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml index 96e30744d24..25b0593d63c 100644 --- a/docs/diagrams/UndoRedoState0.puml +++ b/docs/diagrams/UndoRedoState0.puml @@ -6,9 +6,9 @@ skinparam ClassBorderColor #000000 title Initial state package States { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ef0:EzFoodie__" + class State2 as "__ef1:EzFoodie__" + class State3 as "__ef2:EzFoodie__" } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 diff --git a/docs/diagrams/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml index 01fcb9b2b96..802c8712866 100644 --- a/docs/diagrams/UndoRedoState1.puml +++ b/docs/diagrams/UndoRedoState1.puml @@ -3,12 +3,12 @@ skinparam ClassFontColor #000000 skinparam ClassBorderColor #000000 -title After command "delete 5" +title After command "del -mem/ -i/5" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ef0:EzFoodie__" + class State2 as "__ef1:EzFoodie__" + class State3 as "__ef2:EzFoodie__" } State1 -[hidden]right-> State2 diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml index bccc230a5d1..131118acf20 100644 --- a/docs/diagrams/UndoRedoState2.puml +++ b/docs/diagrams/UndoRedoState2.puml @@ -3,12 +3,12 @@ skinparam ClassFontColor #000000 skinparam ClassBorderColor #000000 -title After command "add n/David" +title After command "add -mem/ -n/John Doe ..." package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ef0:EzFoodie__" + class State2 as "__ef1:EzFoodie__" + class State3 as "__ef2:EzFoodie__" } State1 -[hidden]right-> State2 diff --git a/docs/diagrams/UndoRedoState3.puml b/docs/diagrams/UndoRedoState3.puml index ea29c9483e4..8b6f6fc8d18 100644 --- a/docs/diagrams/UndoRedoState3.puml +++ b/docs/diagrams/UndoRedoState3.puml @@ -6,9 +6,9 @@ skinparam ClassBorderColor #000000 title After command "undo" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ef0:EzFoodie__" + class State2 as "__ef1:EzFoodie__" + class State3 as "__ef2:EzFoodie__" } State1 -[hidden]right-> State2 diff --git a/docs/diagrams/UndoRedoState4.puml b/docs/diagrams/UndoRedoState4.puml index 1b784cece80..1fcfaa07921 100644 --- a/docs/diagrams/UndoRedoState4.puml +++ b/docs/diagrams/UndoRedoState4.puml @@ -3,12 +3,12 @@ skinparam ClassFontColor #000000 skinparam ClassBorderColor #000000 -title After command "list" +title After command "list -mem/" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ef0:EzFoodie__" + class State2 as "__ef1:EzFoodie__" + class State3 as "__ef2:EzFoodie__" } State1 -[hidden]right-> State2 diff --git a/docs/diagrams/UndoRedoState5.puml b/docs/diagrams/UndoRedoState5.puml index 88927be32bc..9e344199920 100644 --- a/docs/diagrams/UndoRedoState5.puml +++ b/docs/diagrams/UndoRedoState5.puml @@ -6,9 +6,9 @@ skinparam ClassBorderColor #000000 title After command "clear" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab3:AddressBook__" + class State1 as "__ef0:EzFoodie__" + class State2 as "__ef1:EzFoodie__" + class State3 as "__ef2:EzFoodie__" } State1 -[hidden]right-> State2 @@ -17,5 +17,5 @@ State2 -[hidden]right-> State3 class Pointer as "Current State" #FFFFF Pointer -up-> State3 -note right on link: State ab2 deleted. +note right on link: State ef2 deleted. @end diff --git a/docs/diagrams/UndoSequenceDiagram.puml b/docs/diagrams/UndoSequenceDiagram.puml index 410aab4e412..45d8b2ab57c 100644 --- a/docs/diagrams/UndoSequenceDiagram.puml +++ b/docs/diagrams/UndoSequenceDiagram.puml @@ -3,42 +3,42 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR -participant "u:UndoCommand" as UndoCommand LOGIC_COLOR +participant ":EzFoodieParser" as EzFoodieParser LOGIC_COLOR +participant "command:UndoCommand" as UndoCommand LOGIC_COLOR end box box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR -participant ":VersionedAddressBook" as VersionedAddressBook MODEL_COLOR +participant ":VersionedEzFoodie" as VersionedEzFoodie MODEL_COLOR end box -[-> LogicManager : execute(undo) +[-> LogicManager : execute("undo") activate LogicManager -LogicManager -> AddressBookParser : parseCommand(undo) -activate AddressBookParser +LogicManager -> EzFoodieParser : parseCommand("undo") +activate EzFoodieParser create UndoCommand -AddressBookParser -> UndoCommand +EzFoodieParser -> UndoCommand activate UndoCommand -UndoCommand --> AddressBookParser +UndoCommand --> EzFoodieParser : command deactivate UndoCommand -AddressBookParser --> LogicManager : u -deactivate AddressBookParser +EzFoodieParser --> LogicManager : command +deactivate EzFoodieParser -LogicManager -> UndoCommand : execute() +LogicManager -> UndoCommand : execute(model) activate UndoCommand -UndoCommand -> Model : undoAddressBook() +UndoCommand -> Model : undoEzFoodie() activate Model -Model -> VersionedAddressBook : undo() -activate VersionedAddressBook +Model -> VersionedEzFoodie : undo() +activate VersionedEzFoodie -VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook) -VersionedAddressBook --> Model : -deactivate VersionedAddressBook +VersionedEzFoodie -> VersionedEzFoodie :resetData(ReadOnlyEzFoodie) +VersionedEzFoodie --> Model : +deactivate VersionedEzFoodie Model --> UndoCommand deactivate Model diff --git a/docs/images/AddReservationSequenceDiagram.png b/docs/images/AddReservationSequenceDiagram.png new file mode 100644 index 00000000000..eee5da3dd07 Binary files /dev/null and b/docs/images/AddReservationSequenceDiagram.png differ diff --git a/docs/images/AddTransactionSequenceDiagram.png b/docs/images/AddTransactionSequenceDiagram.png new file mode 100644 index 00000000000..66a27139e4a Binary files /dev/null and b/docs/images/AddTransactionSequenceDiagram.png differ diff --git a/docs/images/ArchitectureDiagram.png b/docs/images/ArchitectureDiagram.png index 86c60246ccb..6b5cb64258d 100644 Binary files a/docs/images/ArchitectureDiagram.png and b/docs/images/ArchitectureDiagram.png differ diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 2f1346869d0..3e87321ef68 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png index 1ec62caa2a5..a46342d3a11 100644 Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ diff --git a/docs/images/CommitActivityDiagram.png b/docs/images/CommitActivityDiagram.png index c08c13f5c8b..bf3195902a3 100644 Binary files a/docs/images/CommitActivityDiagram.png and b/docs/images/CommitActivityDiagram.png differ diff --git a/docs/images/ComponentManagers.png b/docs/images/ComponentManagers.png index 40f20323674..9df249add8f 100644 Binary files a/docs/images/ComponentManagers.png and b/docs/images/ComponentManagers.png differ diff --git a/docs/images/DeleteReservationSequenceDiagram.png b/docs/images/DeleteReservationSequenceDiagram.png new file mode 100644 index 00000000000..1df83dff68a Binary files /dev/null and b/docs/images/DeleteReservationSequenceDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index fa327b39618..132f9b4f890 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/FindActivityDiagram.png b/docs/images/FindActivityDiagram.png new file mode 100644 index 00000000000..f3f4b01707c Binary files /dev/null and b/docs/images/FindActivityDiagram.png differ diff --git a/docs/images/FindSequenceDiagram.png b/docs/images/FindSequenceDiagram.png new file mode 100644 index 00000000000..962de9b9d30 Binary files /dev/null and b/docs/images/FindSequenceDiagram.png differ diff --git a/docs/images/Launch.png b/docs/images/Launch.png new file mode 100644 index 00000000000..58f8d07d4ca Binary files /dev/null and b/docs/images/Launch.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index c3028aa1cda..40d82e0a516 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/Logo.png b/docs/images/Logo.png new file mode 100644 index 00000000000..8d534e08377 Binary files /dev/null and b/docs/images/Logo.png differ diff --git a/docs/images/MarkHelp.png b/docs/images/MarkHelp.png new file mode 100644 index 00000000000..c0ae799db91 Binary files /dev/null and b/docs/images/MarkHelp.png differ diff --git a/docs/images/MarkShow.png b/docs/images/MarkShow.png new file mode 100644 index 00000000000..8ed8e807c64 Binary files /dev/null and b/docs/images/MarkShow.png differ diff --git a/docs/images/MarkSummary.png b/docs/images/MarkSummary.png new file mode 100644 index 00000000000..06a5d8ca2de Binary files /dev/null and b/docs/images/MarkSummary.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 39d7aec4b33..42ea169c1d0 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png index 58ad22ce16a..0813d9915c7 100644 Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ diff --git a/docs/images/ParserClassesCase1.png b/docs/images/ParserClassesCase1.png new file mode 100644 index 00000000000..56ee72ee37f Binary files /dev/null and b/docs/images/ParserClassesCase1.png differ diff --git a/docs/images/ParserClassesCase2.png b/docs/images/ParserClassesCase2.png new file mode 100644 index 00000000000..9a1ece88f81 Binary files /dev/null and b/docs/images/ParserClassesCase2.png differ diff --git a/docs/images/SortActivityDiagram.png b/docs/images/SortActivityDiagram.png new file mode 100644 index 00000000000..6c0a6cd0eda Binary files /dev/null and b/docs/images/SortActivityDiagram.png differ diff --git a/docs/images/SortSequenceDiagram.png b/docs/images/SortSequenceDiagram.png new file mode 100644 index 00000000000..b915683620c Binary files /dev/null and b/docs/images/SortSequenceDiagram.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 82c66f8f16e..25106e71d16 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/SummarySequenceDiagram.png b/docs/images/SummarySequenceDiagram.png new file mode 100644 index 00000000000..7851d7ec70a Binary files /dev/null and b/docs/images/SummarySequenceDiagram.png differ diff --git a/docs/images/UIMarkShow.png b/docs/images/UIMarkShow.png new file mode 100644 index 00000000000..794a987f1c7 Binary files /dev/null and b/docs/images/UIMarkShow.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 91488fd1a0f..b082126d54a 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagramHelpWindow.png b/docs/images/UiClassDiagramHelpWindow.png new file mode 100644 index 00000000000..54f372a0909 Binary files /dev/null and b/docs/images/UiClassDiagramHelpWindow.png differ diff --git a/docs/images/UiClassDiagramMainWindow.png b/docs/images/UiClassDiagramMainWindow.png new file mode 100644 index 00000000000..fd0733bfa8b Binary files /dev/null and b/docs/images/UiClassDiagramMainWindow.png differ diff --git a/docs/images/UiClassDiagramMemberListPanel.png b/docs/images/UiClassDiagramMemberListPanel.png new file mode 100644 index 00000000000..caedb650b3a Binary files /dev/null and b/docs/images/UiClassDiagramMemberListPanel.png differ diff --git a/docs/images/UiClassDiagramMemberViewWindow.png b/docs/images/UiClassDiagramMemberViewWindow.png new file mode 100644 index 00000000000..def71a3a1ca Binary files /dev/null and b/docs/images/UiClassDiagramMemberViewWindow.png differ diff --git a/docs/images/UiClassDiagramSummaryWindow.png b/docs/images/UiClassDiagramSummaryWindow.png new file mode 100644 index 00000000000..7f92f163954 Binary files /dev/null and b/docs/images/UiClassDiagramSummaryWindow.png differ diff --git a/docs/images/UndoRedoState0.png b/docs/images/UndoRedoState0.png index 8f7538cd884..ada48bb5110 100644 Binary files a/docs/images/UndoRedoState0.png and b/docs/images/UndoRedoState0.png differ diff --git a/docs/images/UndoRedoState1.png b/docs/images/UndoRedoState1.png index df9908d0948..9ec102cfa49 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 36519c1015b..727d80f18ff 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 19959d01712..7fcf064015a 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 4c623e4f2c5..e59d7664f79 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 84ad2afa6bd..7accf8a01f8 100644 Binary files a/docs/images/UndoRedoState5.png and b/docs/images/UndoRedoState5.png differ diff --git a/docs/images/UndoSequenceDiagram.png b/docs/images/UndoSequenceDiagram.png index 6addcd3a8d9..c5be02a5da0 100644 Binary files a/docs/images/UndoSequenceDiagram.png and b/docs/images/UndoSequenceDiagram.png differ diff --git a/docs/images/downloadInstruction.png b/docs/images/downloadInstruction.png new file mode 100644 index 00000000000..24980ce15b6 Binary files /dev/null and b/docs/images/downloadInstruction.png differ diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png index b1f70470137..8f2d20f1bcf 100644 Binary files a/docs/images/helpMessage.png and b/docs/images/helpMessage.png differ diff --git a/docs/images/holmesjj.png b/docs/images/holmesjj.png new file mode 100644 index 00000000000..8d7b8968d3e Binary files /dev/null and b/docs/images/holmesjj.png differ diff --git a/docs/images/homefolderEzFoodie.png b/docs/images/homefolderEzFoodie.png new file mode 100644 index 00000000000..742671b6dcd Binary files /dev/null and b/docs/images/homefolderEzFoodie.png differ diff --git a/docs/images/morrow1ndy.png b/docs/images/morrow1ndy.png new file mode 100644 index 00000000000..2e608c6fd13 Binary files /dev/null and b/docs/images/morrow1ndy.png differ diff --git a/docs/images/mukundrs.png b/docs/images/mukundrs.png new file mode 100644 index 00000000000..2b7fea42d16 Binary files /dev/null and b/docs/images/mukundrs.png differ diff --git a/docs/images/popup_error_example.png b/docs/images/popup_error_example.png new file mode 100644 index 00000000000..f7aa42a4f5c Binary files /dev/null and b/docs/images/popup_error_example.png differ diff --git a/docs/images/stephanie-csy.png b/docs/images/stephanie-csy.png new file mode 100644 index 00000000000..b4fdceab3e1 Binary files /dev/null and b/docs/images/stephanie-csy.png differ diff --git a/docs/images/zzybluebell.png b/docs/images/zzybluebell.png new file mode 100644 index 00000000000..386e37bcbd3 Binary files /dev/null and b/docs/images/zzybluebell.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..d4378f0a24c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,19 +1,32 @@ --- layout: page -title: AddressBook Level-3 +title: ezFoodie --- -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) -[![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3) +[![CI Status](https://github.com/AY2122S1-CS2103T-F12-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2122S1-CS2103T-F12-4/tp/actions) +[![codecov](https://codecov.io/gh/AY2122S1-CS2103T-F12-4/tp/branch/master/graph/badge.svg)](https://codecov.io/gh/AY2122S1-CS2103T-F12-4/tp) ![Ui](images/Ui.png) -**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). +* Have you faced some issues when managing the members in your restaurant? `ezFoodie` is here! -* 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. +* `ezFoodie` is a desktop application that helps restaurants **keep track of their ever-growing list of members**. It is optimized for using via a **Command Line Interface (CLI)** while still having the benefits of a **Graphical User Interface (GUI)**. If you can type fast, ezFoodie can get your member management tasks done faster than traditional GUI applications. +* If you are interested in using `ezFoodie`, head over to the [_Quick Start_ section of the **User Guide**](https://ay2122s1-cs2103t-f12-4.github.io/tp/UserGuide.html#quick-start). -**Acknowledgements** +* If you are interested in developing `ezFoodie`, the [**Developer Guide**](https://ay2122s1-cs2103t-f12-4.github.io/tp/DeveloperGuide.html) is a good place to start. -* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5) +## Acknowledgements + +This project developed based on the **[Address Book Product Website](https://se-education.org/addressbook-level3)** project. Which is a part of the se-education.org initiative. + +The icons of the project were obtained from [ezfoodie_icon](https://www.brandcrowd.com/), [member_icon](https://www.percici.com/), [summary_icon](https://www.pngwing.com/) + +Libraries used: + +* [JavaFX](https://openjfx.io/) +* [Jackson](https://github.com/FasterXML/jackson) +* [JUnit5](https://github.com/junit-team/junit5) +* [PlantUML](https://plantuml.com/) + +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 information. diff --git a/docs/team/holmesjj.md b/docs/team/holmesjj.md new file mode 100644 index 00000000000..6ae912b45aa --- /dev/null +++ b/docs/team/holmesjj.md @@ -0,0 +1,158 @@ +--- +layout: page +title: Hu Jiajun's Project Portfolio Page +--- + +### Introduction + +This page serves to document my contributions to the project ezFoodie under NUS module CS2103T in AY21/22 semester 1. + +### Project: ezFoodie + +ezFoodie is a desktop application that helps restaurants **keep track of their ever-growing list of members**. +Restaurant managers and staffs can easily view and update member status (e.g. personal information, tier, reservation, transaction, etc.) to manage and analyze members. +Restaurant managers and staffs interact with ezFoodie using a Command Line Interface (CLI), and has a Graphical User Interface (GUI) created with JavaFX. + +It is written in Java, and has about 35 kLoC, of which I contributed about 10 kLoC. + +Given below are my contributions to the project. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?breakdown=true&search=holmesjj) + +* **New Model**: Created the `Account` model to support implementing `login` and `logout` features as a manager. [\#74](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/74) + * **What it does**: stores the `Account` information for the manager to `login`. + * **Justification**: This model is essential, since some advanced features such as `delete` and `sort` are required the manager permission. It is a bridge between the staff and manager. + * **Highlights**: This enhancement requires understanding on how `hash` works. The implementation was challenging as the `Account` information need to be hashed when it is storing in the file for the purpose of high security. + +* **New Feature**: Added the ability to `login` as a manager [\#74](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/74) + * **What it does**: allows the manager to `login` as a manager. + * **Justification**: The staff and manager should not have the full permission to control the application. Some advanced features such as `delete` and `sort` should only be accessed by manager. + +* **New Feature**: Added the ability to `logout` as a manager [\#72](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/72) + * **What it does**: allows the manager to `logout` as a manager. + * **Justification**: The manager should be able to `logout` the application to prevent the staff from operating the advanced features such as `delete` and `sort` that it should only be accessed by manager. + +* **New Feature**: Added the ability to `edit` `Transaction` by `Transaction ID` [\#75](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/75), [\#112](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/112) + * **What it does**: allows the staff and manager to `edit` `Transaction` by `Transaction ID`. + * **Justification**: The staff and manager should be able to correct any accidental mistakes conveniently when he/she adds some incorrect `Transaction` records. + * **Highlights**: This enhancement requires understanding on how polymorphism works and implement it so that the application will use Object-oriented programming (OOP) sufficiently. The implementation was challenging as the `EditCommand` need to be abstracted, and inherit `EditCommand` by `EditCommandPrefixParser`, and further inherit `EditCommandPrefixParser` by `EditTransactionCommand`. Similarly, the `EditCommandParser` also need to be abstracted, and inherit it with `EditTransactionCommandParser`. + +* **New Feature**: Added the ability to `edit` `Reservation` by `Reservation ID` [\#114](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/114) + * **What it does**: allows the staff and manager to `edit` `Reservation` by `Reservation ID`. + * **Justification**: The staff and manager should be able to correct any accidental mistakes conveniently when he/she adds some incorrect `Reservation` records. + * **Highlights**: This enhancement requires understanding on how polymorphism works and implement it so that the application will use Object-oriented programming (OOP) sufficiently. The implementation was challenging as the `EditCommand` need to be abstracted, and inherit `EditCommand` by `EditCommandPrefixParser`, and further inherit `EditCommandPrefixParser` by `EditReservationCommand`. Similarly, the `EditCommandParser` also need to be abstracted, and inherit it with `EditReservationCommandParser`. + +* **New Feature**: Added the ability to `delete` `Member` by `Member ID` [\#77](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/77) + * **What it does**: allows the manager to `delete` `Member` by `Member ID`. + * **Justification**: This feature is essential, since the `Transaction` and `Reservation` is based on each member, it is necessary to use `Member ID` to identify the `Member` so that his/her `Transaction` and `Reservation` can be identified properly as well (Currently the application is using `Member ID` + `Transaction ID` to retrieve the `Transaction`, so is `Reservation`). + +* **New Feature**: Added the ability to `delete` `Transaction` by `Transaction ID` [\#101](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/101) + * **What it does**: allows the manager to `delete` `Transaction` by `Transaction ID` + * **Justification**: This feature is essential, since each `Transaction` has its own `ID`, and the application is not able to list all the `Transactions`, it is impossible to `delete` `Transaction` by `Index`. Therefore, the `Transaction ID` is the only attribute to identify the `Transaction`. + * **Highlights**: This enhancement requires understanding on how polymorphism works and implement it so that the application will use Object-oriented programming (OOP) sufficiently. The implementation was challenging as the `DeleteCommand` need to be abstracted, and inherit `DeleteCommand` by `DeleteCommandPrefixParser`, and further inherit `DeleteCommandPrefixParser` by `DeleteTransactionCommand`. Similarly, the `DeleteCommandParser` also need to be abstracted, and inherit it with `DeleteTransactionCommandParser`. + +* **New Feature**: Added the ability to `sort` `Member` by `Credit` [\#79](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/79) + * **What it does**: allows the manager to `sort` `Member` by `Credit`. + * **Justification**: This feature improves the product significantly because the manager can formulate promotional strategies by analyzing the member data like how many credit the member has earned. + * **Highlights**: This enhancement requires understanding on how `javafx.collections` package works. The implementation was challenging as it requires combining and synchronizing the object of the `FilteredList` and the `SortedList` so that data will not be messed up when the `find` or `sort` feature is called. + +* **New Feature**: Added the ability to `retrieve` history commands [\#113](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/113) + * **What it does**: allows the staff and manager to `retrieve` previous commands using `up`/`down` keys. + * **Justification**: This feature improves the product significantly because the staff and manager can execute the similar commands much faster without retyping. + +* **New Feature**: Added the ability to escape `summary`, `show` and `help` window [\#194](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/194) + * **What it does**: allows the staff and manager to escape `summary`, `show` or `help` window using `esc` key. + * **Justification**: This feature is helpful to improve the use efficiency because the staff and manager do not need to use the mouse to exit the window. + +* **New Feature**: Updated a neater and better UI for the `show` feature [\#194](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/194) + * **What it does**: allows the staff and manager to view the member details more clearly. + * **Justification**: This feature improves the product significantly because the manager will be very easy to view the member details, such as transactions and reservations records. + +* **Enhancements to existing models**: + * Updated the `AddressBook` to the `ezFoodie` [\#45](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/45) + * Updated the `Person` model to the `Member` model [\#48](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/48) + * Added the `ID` for the `Member` model so that the staff and manager can do operations based on the `Member ID` [\#51](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/51) + * Added the `ID` for the `Transaction` model so that the staff and manager can do operations based on the `Transaction ID` [\#100](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/100) + * Added the `Timestamp` for the `Member` model and `Transaction` model so that the application can record down the registration `Timestamp` of the `Member` and the payment `Timestamp` of the `Transaction` [\#59](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/59) + * Added the `Credit` for the `Member` model so that the application can record down the `Credit` that the `Member` has earned. [\#79](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/79) + * Added the `Tier` for the `Member` model so that the application can show the `Tier` of the `Member` based on his/her `Credit`. [\#81](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/81) + +* **Enhancements to existing features**: + * Updated the `add` feature so that it can supoort `add` member ID [\#55](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/55) + * Updated the `list` feature so that it can supoort `list -mem` instead of `list` [\#78](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/78) + * Updated the `find` feature so that it can supoort `find` `Member` by `Member ID`, `Name`, `Phone`, `Email` and `Registration Date` [\#64](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/64) + +* **Project management**: + * Maintained the [issues](https://github.com/AY2122S1-CS2103T-F12-4/tp/issues), [milestones](https://github.com/AY2122S1-CS2103T-F12-4/tp/milestones) and [projects](https://github.com/AY2122S1-CS2103T-F12-4/tp/projects) + * Managed releases `v1.2.1`, `v1.3`, `v1.4` (3 [releases](https://github.com/AY2122S1-CS2103T-F12-4/tp/releases)) on GitHub + +* **Documentation**: + * index: + * Fixed hyperlink bugs [\#89](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/89) + * README: + * Updated formats [\#42](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/42) + * User Guide: + * Added documentation for the features `find` and `sort` + [\#89](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/89) + * Updated formats and fixed bugs + [\#40](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/40), + [\#41](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/41) + * Updated formats for all commands [\#116](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/116) + * Developer Guide: + * Updated `Product scope`, `User stories`, `Use cases`, `Non-Functional Requirements`, `Glossary` + [\#24](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/24) + * Added documentation for the features `find` and `sort` + [\#89](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/89) + * Updated all diagrams and descriptions including sequence diagram, class diagram, activity diagram, etc. + * Updated `User stories` and `Use cases` + [\#204](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/204) + * Added `Appendix 3: Effort` and `Appendix 4: Limitations and Future improvements` + [\#207](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/207) + * About Us: + * Updated formats [\#43](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/43) + +* **Community**: + * PRs reviewed (with non-trivial review comments): + [\#46](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/46), + [\#52](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/52), + [\#56](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/56), + [\#69](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/69), + [\#71](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/71), + [\#102](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/102), + [\#103](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/103), + [\#120](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/200) + * Contributed to forum discussions: + [\#12](https://github.com/nus-cs2103-AY2122S1/forum/issues/12), + [\#13](https://github.com/nus-cs2103-AY2122S1/forum/issues/13#issuecomment-899985208), + [\#24](https://github.com/nus-cs2103-AY2122S1/forum/issues/24#issuecomment-899953855), + [\#45](https://github.com/nus-cs2103-AY2122S1/forum/issues/45), + [\#50](https://github.com/nus-cs2103-AY2122S1/forum/issues/50), + [\#65](https://github.com/nus-cs2103-AY2122S1/forum/issues/65), + [\#109](https://github.com/nus-cs2103-AY2122S1/forum/issues/109#issuecomment-907304027), + [\#141](https://github.com/nus-cs2103-AY2122S1/forum/issues/141#issuecomment-910317016), + [\#160](https://github.com/nus-cs2103-AY2122S1/forum/issues/160#issuecomment-909923810), + [\#200](https://github.com/nus-cs2103-AY2122S1/forum/issues/200#issuecomment-914391124), + [\#203](https://github.com/nus-cs2103-AY2122S1/forum/issues/203#issuecomment-914375528), + [\#209](https://github.com/nus-cs2103-AY2122S1/forum/issues/209) + * Reported bugs and suggestions for other teams in the class: + [\#1](https://github.com/holmesjj/ped/issues/1), + [\#2](https://github.com/holmesjj/ped/issues/2), + [\#3](https://github.com/holmesjj/ped/issues/3), + [\#4](https://github.com/holmesjj/ped/issues/4), + [\#5](https://github.com/holmesjj/ped/issues/5), + [\#6](https://github.com/holmesjj/ped/issues/6), + [\#7](https://github.com/holmesjj/ped/issues/7), + [\#8](https://github.com/holmesjj/ped/issues/8), + [\#9](https://github.com/holmesjj/ped/issues/9), + [\#10](https://github.com/holmesjj/ped/issues/10), + [\#11](https://github.com/holmesjj/ped/issues/11), + [\#12](https://github.com/holmesjj/ped/issues/12), + [\#13](https://github.com/holmesjj/ped/issues/13), + [\#14](https://github.com/holmesjj/ped/issues/14), + [\#15](https://github.com/holmesjj/ped/issues/15), + [\#16](https://github.com/holmesjj/ped/issues/16), + [\#17](https://github.com/holmesjj/ped/issues/17), + [\#18](https://github.com/holmesjj/ped/issues/18), + [\#19](https://github.com/holmesjj/ped/issues/19), + [\#20](https://github.com/holmesjj/ped/issues/20), + [\#21](https://github.com/holmesjj/ped/issues/21) diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md deleted file mode 100644 index 773a07794e2..00000000000 --- a/docs/team/johndoe.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -layout: page -title: John Doe's Project Portfolio Page ---- - -### Project: AddressBook Level 3 - -AddressBook - Level 3 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. - -Given below are my contributions to the project. - -* **New Feature**: Added the ability to undo/redo previous commands. - * What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command. - * Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them. - * Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands. - * Credits: *{mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}* - -* **New Feature**: Added a history command that allows the user to navigate to previous commands using up/down keys. - -* **Code contributed**: [RepoSense link]() - -* **Project management**: - * Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub - -* **Enhancements to existing features**: - * Updated the GUI color scheme (Pull requests [\#33](), [\#34]()) - * Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests [\#36](), [\#38]()) - -* **Documentation**: - * User Guide: - * Added documentation for the features `delete` and `find` [\#72]() - * Did cosmetic tweaks to existing documentation of features `clear`, `exit`: [\#74]() - * Developer Guide: - * Added implementation details of the `delete` feature. - -* **Community**: - * PRs reviewed (with non-trivial review comments): [\#12](), [\#32](), [\#19](), [\#42]() - * Contributed to forum discussions (examples: [1](), [2](), [3](), [4]()) - * Reported bugs and suggestions for other teams in the class (examples: [1](), [2](), [3]()) - * Some parts of the history feature I added was adopted by several other class mates ([1](), [2]()) - -* **Tools**: - * Integrated a third party library (Natty) to the project ([\#42]()) - * Integrated a new Github plugin (CircleCI) to the team repo - -* _{you can add/remove categories in the list above}_ diff --git a/docs/team/morrow1ndy.md b/docs/team/morrow1ndy.md new file mode 100644 index 00000000000..5984cb13d62 --- /dev/null +++ b/docs/team/morrow1ndy.md @@ -0,0 +1,135 @@ +--- +layout: page +title: Yang Yuzhao's Project Portfolio Page +--- + +### Introduction + +This page serves to document my contributions to the project ezFoodie under NUS module CS2103T in AY21/22 semester 1. + +### Project: ezFoodie + +ezFoodie is a desktop application that helps restaurants **keep track of their ever-growing list of members**. +Restaurant managers and staffs can easily view and update member status (e.g. personal information, tier, reservation, transaction, etc.) to manage and analyze members. +Restaurant managers and staffs interact with ezFoodie using a Command Line Interface (CLI), and has a Graphical User Interface (GUI) created with JavaFX. + +It is written in Java, and has about 35 kLoC, of which I contributed about 2.1 kLoC. + +Given below are my contributions to the project. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?breakdown=true&search=morrow1ndy) + + +* **New Feature**: Added the ability to `add` `Transaction` by `Member ID` [\#93](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/93), [\#90](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/90), [\#73](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/73). + + * **What it does**: Allows the staff and manager to `add` `Transaction` to a specified member, + identified by his `Member ID`. + + * **Justification**: The staff and manager should be able to add a new `Tranasction` to ezFoodie + when it is made by a member at the restaurant. + + * **Highlights**: This feature requires an understanding on how inheritance and polymorphism in OOP is used, + since `AddMember` command and `AddTransaction` command share the same command word `add`. In the first implementation, + `AddMember` command and `AddTransaction` command both inherit from `AddCommand`, + and the same for two `Parser` classes of these two commands. + `AddCommand` are parsed by `AddCommandPrefixParser` first + to distinguish between two commands before any further actions. + This obeys the original code structure. + + +* **New Feature**: Added the ability to `show` `Member` details by `Member ID` in a separate pop-up window + [\#102](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/102) + *(The Java class is named `ViewCommand`, while the command is named `show` to fit the command naming in our UG.)*. + + * **What it does**: Allows the staff and manager to `show` a specific `Member`'s details + that are not shown in main window, such as `Transaction` and `Reservation`. + The member is identified by his `Member ID`. + + * **Justification**: It is too long and tedious if all information is shown in `MainWindow` and is not user-friendly. + Moreover, viewing a `Member`'s details is most probably a one time action in daily restaurant routine. + Hence, `MainWindow` should remain open, and a separate pop-up window would be a good choice for staff and managers to + `show` a specific `Member`'s `Transaction` and `Reservation` details when they want to. + + * **Highlights**: This feature requires an understanding on full stack integration and object references in Java. + `MemberViewWindow` is a new pop-up window and hence it should be a separate stage in JavaFX. + `Model` and `CommandResult` are changed according to include a boolean value, + indicating whether `MemberViewWindow` should be displayed or hidden. + Since the feature uses the same `filterMemberList` method as `FindCommand` due to abstraction and encapsulation, + a deep copy of `updatedMemberList` is used for `MemberViewWindow` usage only, so that when the user asks for + a specific `Member`'s details, the deep copy is updated instead and + the copied `updatedMemberList` shown in `MemberViewWindow` will not affect the + `MemberListPanel` *(which uses the original `updatedMemberList` and refers to all members stored in ezFoodie)* + shown in `MainWindow`. + + +* **New Feature**: Added the ability to view `summary` of data stored in ezFoodie in a separate pop-up window. + [\#111](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/111) + + * **What it does**: Allows managers to view `summary` of + total `Member` existing in ezFoodie as well as time-series statistics of `Transaction` data + *(i.e. aggregate number and amount of transactions recorded in ezFoodie in past 1/3/6 months)*. + + * **Justification**: This a manger-restricted function. Managers should have the ability to view the summary of + essential statistics to get an understanding of how well the restaurant is performing. + This information should not be visible in `MainWindow` as permission is required to view `summary`. + Moreover, it may be too troublesome for managers to type another `list` command to get back to view `MemberListPanel` + after viewing `summary`. + Hence, a pop-up window is a good choice for `summary`. + + * **Highlights**: This feature requires an understanding on full stack integration. + User needs to log-in as a manager first before it can perform view `summary` action in ezFoodie. + `SummaryWindow` is a new pop-up window and hence it should be a separate stage in JavaFX. + `Model` and `CommandResult` are changed according to include a boolean value, + indicating whether `SummaryWindow` should be displayed or hidden. + + +* **Enhancements to existing features**: + * Updated the `help` feature to abstract `HelpBox` from `HelpWindow` for further abstraction(OOP). + [\#200](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/200) + * Updated the `help` feature for a prettier GUI of `HelpWindow` by modifying layouts, fonts and colors used. + [\#200](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/200) + * Updated the `summary` feature to abstract `SummaryBox` from `SummaryWindow` for further abstraction(OOP). + [\#200](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/200) + * Updated the `ReservationPanel` in `summary` feature to sort the reservations of a specific member in correct order + [\#200](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/200) + * Updated the `MainWindow` layout for a prettier GUI. + [\#200](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/200) + +* **Project management**: + * Maintained the [issues](https://github.com/AY2122S1-CS2103T-F12-4/tp/issues), + [milestones](https://github.com/AY2122S1-CS2103T-F12-4/tp/milestones) and + [projects](https://github.com/AY2122S1-CS2103T-F12-4/tp/projects) + * Managed releases `v1.2.1`, `v1.3`, `v1.4` ([releases](https://github.com/AY2122S1-CS2103T-F12-4/tp/releases)), + and largely contributed to wrap-up of `v1.3` before the project demo and pitch. + +* **Documentation**: + * AboutUs: + * Updated AboutUs for member information. + [\#29](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/29) + [\#31](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/31) + [\#36](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/36) + [\#39](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/39) + * User Guide: + * Added documentation for the features `edit` and bug fixes. + [\#62](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/62) + [\#191](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/191) + * Developer Guide: + * Added documentation for the features `addTransaction` and `Summary` and `ShowMemberProfile` + [\#216](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/216) + +* **Community**: + * PRs reviewed (with non-trivial review comments): + [\#217](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/217), + [\#208](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/208), + [\#206](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/206), + [\#207](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/207), + [\#203](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/203), + [\#199](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/199), + [\#103](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/103) + * Reported bugs and suggestions for other teams in the class: + [\#1](https://github.com/morrow1ndy/ped/issues/1), + [\#2](https://github.com/morrow1ndy/ped/issues/2), + [\#3](https://github.com/morrow1ndy/ped/issues/3), + [\#4](https://github.com/morrow1ndy/ped/issues/4), + [\#5](https://github.com/morrow1ndy/ped/issues/5), + [\#6](https://github.com/morrow1ndy/ped/issues/6) diff --git a/docs/team/mukundrs.md b/docs/team/mukundrs.md new file mode 100644 index 00000000000..aa458dd9843 --- /dev/null +++ b/docs/team/mukundrs.md @@ -0,0 +1,71 @@ +--- +layout: page +title: Raja Sudalaimuthu Mukund's Project Portfolio Page +--- + +### Introduction + +This page serves to document my contributions to the project ezFoodie under NUS module CS2103T in AY21/22 semester 1. + +### Project: ezFoodie + +ezFoodie is a desktop application that helps restaurants **keep track of their ever-growing list of members**. +Restaurant managers and staffs can easily view and update member status (e.g. personal information, tier, reservation, transaction, etc.) to manage and analyze members. +Restaurant managers and staffs interact with ezFoodie using a Command Line Interface (CLI), and has a Graphical User Interface (GUI) created with JavaFX. + +It is written in Java, and has about 35 kLoC, of which I contributed about 1 kLoC. + +Given below are my contributions to the project. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/#breakdown=true&search=mukundrs) + +* **New Model**: Created the `Reservation` model to support adding reservation details for each member. [\#94](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/94) + * **What it does**: stores the details of each reservation under each `member`. + * **Justification**: This model is essential as it is one of the core features of ezFoodie. + * **Highlights**: This enhancement requires understanding on how `hash` works. The implementation was challenging as the `Account` information need to be hashed when it is storing in the file for the purpose of high security. + +* **New Feature**: Added the ability to `add``Reservation` to a member. [\#94](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/94) + * **What it does**: adds a new reservation to the reservation list of a `member`. + * **Justification**: This feature is essential as it is one of the core features of ezFoodie. + * **Highlights**: This enhancement requires understanding on how polymorphism works and implement it so that the application will use Object-oriented programming (OOP) sufficiently. The implementation was challenging as the `AddCommand` need to be abstracted, and inherit `AddCommand` by `AddCommandPrefixParser`, and further inherit `AddCommandPrefixParser` by `AddReservationCommand`. Similarly, the `AddCommandParser` also need to be abstracted, and inherit it with `AddReservationCommandParser`. + +* **Enhancements to existing features**: + * Updated the help window UI to suit the needs of ezFoodie [\#56](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/56) + +* **Project management**: + * Maintained the [milestones](https://github.com/AY2122S1-CS2103T-F12-4/tp/milestones) + +* **Documentation**: + * User Guide: + * Updated according to changes to code, fixed mistakes + [\#185](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/185), + [\#193](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/193), + [\#199](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/199), + [\#208](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/208), + [\#218](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/218) + * Developer Guide: + * Added documentation for add reservation feature, fixed mistakes and bugs + [\#199](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/199), + [\#218](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/218) + +* **Community**: + * PRs reviewed (with non-trivial review comments): + [\217](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/217), + [\215](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/215), + [\212](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/212), + [\207](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/207), + [\205](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/205), + [\204](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/204), + [\197](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/197), + [\55](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/55), + [\51](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/51), + [\48](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/48), + [\45](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/45), + [\37](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/37) + * Reported bugs and suggestions for other teams in the class: + [\#1](https://github.com/mukundrs/ped/issues/1), + [\#2](https://github.com/mukundrs/ped/issues/2), + [\#3](https://github.com/mukundrs/ped/issues/3), + [\#4](https://github.com/mukundrs/ped/issues/4), + [\#5](https://github.com/mukundrs/ped/issues/5), + [\#6](https://github.com/mukundrs/ped/issues/6) diff --git a/docs/team/stephanie-csy.md b/docs/team/stephanie-csy.md new file mode 100644 index 00000000000..afee6c0eb22 --- /dev/null +++ b/docs/team/stephanie-csy.md @@ -0,0 +1,59 @@ +--- +layout: page +title: Stephanie Chen's Project Portfolio Page +--- + +### Introduction + +This page serves to document my contributions to the project ezFoodie under NUS module CS2103T in AY21/22 semester 1. + +### Project: ezFoodie + +ezFoodie is a desktop application that helps restaurants **keep track of their ever-growing list of members**. +Restaurant managers and staffs can easily view and update member status (e.g. personal information, tier, reservation, transaction, etc.) to manage and analyze members. +Restaurant managers and staffs interact with ezFoodie using a Command Line Interface (CLI), and has a Graphical User Interface (GUI) created with JavaFX. + +It is written in Java, and has about 35 kLoC, of which I contributed about 1 kLoC. + +Given below are my contributions to the project. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?breakdown=true&search=stephanie-csy) + +* **New Model**: Created the `Transaction` model to support adding transaction details for each member. [\#69](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/69) + * **What it does**: stores the details of each transaction under each `member`. + * **Justification**: This model is essential as it is one of the core features of ezFoodie. + * **Highlights**: This enhancement requires understanding on how `hash` works. The implementation was challenging as the `Account` information need to be hashed when it is storing in the file for the purpose of high security. + +* **New Feature**: Added the ability to `delete` `Reservation` by `Reservation ID` [\#106](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/106) + * **What it does**: allows the manager to `delete` `Reservation` by `Reservation ID` + * **Justification**: This feature is essential, since each `Reservation` has its own `ID`, and the application is not able to list all the `Reservations`, it is impossible to `delete` `Reservation` by `Index`. Therefore, the `Reservation ID` is the only attribute to identify the `Reservation`. + * **Highlights**: This enhancement requires understanding on how polymorphism works and implement it so that the application will use Object-oriented programming (OOP) sufficiently. The implementation was challenging as the `DeleteCommand` need to be abstracted, and inherit `DeleteCommand` by `DeleteCommandPrefixParser`, and further inherit `DeleteCommandPrefixParser` by `DeleteReservationCommand`. Similarly, the `DeleteCommandParser` also need to be abstracted, and inherit it with `DeleteReservationCommandParser`. + +* **Enhancements to existing models**: + * Added the `ID` for the `Reservation` model so that the staff and manager can do operations based on the `Reservation ID` [\#106](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/106) + +* **Enhancements to existing features**: + * Updated the regex of `Phone` such that it will only accept numbers that are strictly 8 digits long, compared to the original which allowed any number at least 3 digits long. [\#184](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/184) + +* **Project management**: + * Maintained the [milestones](https://github.com/AY2122S1-CS2103T-F12-4/tp/milestones) + +* **Documentation**: + * User Guide: + * Updated according to changes to code, fixed mistakes + [\#197](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/197) + * Developer Guide: + * Added documentation for the `Delete Reservation` feature + [\#205](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/205) + +* **Community**: + * PRs reviewed (with non-trivial review comments): + [\#73](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/73), + [\#94](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/94) + * Reported bugs and suggestions for other teams in the class: + [\#1](https://github.com/stephanie-csy/ped/issues/1), + [\#2](https://github.com/stephanie-csy/ped/issues/2), + [\#3](https://github.com/stephanie-csy/ped/issues/3), + [\#4](https://github.com/stephanie-csy/ped/issues/4), + [\#5](https://github.com/stephanie-csy/ped/issues/5), + [\#6](https://github.com/stephanie-csy/ped/issues/6) diff --git a/docs/team/zzybluebell.md b/docs/team/zzybluebell.md new file mode 100644 index 00000000000..20fd8274b8f --- /dev/null +++ b/docs/team/zzybluebell.md @@ -0,0 +1,103 @@ +--- +layout: page +title: Zhang Zhiyao's Project Portfolio Page +--- + +### Introduction + +This page serves to document my contributions to the project ezFoodie under NUS module CS2103T in AY21/22 semester 1. + +### Project: ezFoodie + +ezFoodie is a desktop application that helps restaurants **keep track of their ever-growing list of members**. +Restaurant managers and staffs can easily view and update member status (e.g. personal information, tier, reservation, transaction, etc.) to manage and analyze members. +Restaurant managers and staffs interact with ezFoodie using a Command Line Interface (CLI), and has a Graphical User Interface (GUI) created with JavaFX. + +It is written in Java, and has about 35 kLoC, of which I contributed about 4 kLoC. + +Given below are my contributions to the project. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?breakdown=true&search=zzybluebell) +
+ +* **Project management**: + * Helps the team to assign the task and review the pull request. +
+ +* **New Feature**: Add Member [\#71](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/71) +* **What it does**: allows the user (manager or staff) to `add` members in the ezFoodie. +* **Justification**: + * The feature is essentials, and the priority is high. + * The staffs and manager should add members based on name, phone, email, address, tags and related information. + * It is necessary to add member by different `Phone` and `Email` of each member. This is only significant way to differentiate the different members. +* **Highlights**: + * This enhancement requires understanding on how polymorphism works and implement it so that the application will use Object-oriented programming (OOP) sufficiently. + * The implementation was challenging as the `AddCommand` need to be abstracted. Similarly, the `AddCommandParser` also need to be abstracted, and inherit it with `AddMemberCommandParser`. +* **Special** Add a few testcases to ensure testing coverage. +
+ +* **New Feature**: Update credits [\#96](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/96) +* **What it does**: Update credit for member in ezFoodie. +* **Justification**: + * Credits refer to the overall accumulated transaction billing of a member, and depends on amounts of billing in add transaction, delete transaction, and edit transaction. + * The feature is essentials, and the priority is medium. + * It also refers to the level of Tier. + * Credit amount ranges from `0` to `99999999`. +* **Highlights**: This enhancement requires understanding on how polymorphism works and implement it so that the application will use Object-oriented programming (OOP) sufficiently. It requires to understand the whole process and code working for add transaction, edit transaction and delete transaction based on the essential contrainsts. +* **Special** Add a few testcases to ensure testing coverage. +
+ +* **New Feature**: Redeem points [\#103](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/103) +* **What it does**: Redeemed as discounts to a member. +* **Justification**: + * The feature is essentials, and the priority is medium. + * The point accumulation is similar to credit. + * However, point can be redeemed as amounts of cash deduction to a member, and point will be deducted accordingly with redemption. + * In delete transaction, the point will not be affected and keep the same. + * In edit transaction, the point will be increased when billing amount is greater than the billing amount added in last time. on the contrary the point will not be affected and keep the same when billing amount is lesser than the billing amount added in last time. + * Point amount is range from `0` to `99999999`. +* **Highlights**: This enhancement requires understanding on how polymorphism works and implement it so that the application will use Object-oriented programming (OOP) sufficiently. It requires to understand the whole process and code working for add transaction, edit transaction and delete transaction based on the essential contrainsts. +* **Special** Add a few testcases to ensure testing coverage. + +* **Documentation**: + + * Javadoc:[\#186](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/186) + * Update the whole public method, public class and releted java file. + + * README:[\#28](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/28) + * Create and edit the first version of README. + + * Index: Editt Index file [\#54](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/54) + + * User Guide:[\#198](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/198) + * Re-design the whol foramt. + * Add the testing pictures. + * Unified terminology. + * etc. + + * Developer Guide: [\#211](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/211) + * Update `add member`, `Redeem Point` features and etc. + + * About Us: [\#206](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/206) + * update essential self-information. + +* **Community**: + * Contributed to commnents: + * PRs reviewed: + [\#202](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/202), + [\#200](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/200), + [\#196](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/196), + [\#193](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/193), + [\#192](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/192), + [\#185](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/185), + [\#183](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/183), + [\#182](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/182), + [\#180](https://github.com/AY2122S1-CS2103T-F12-4/tp/pull/180), + + + * Reported bugs and suggestions for other teams in the class: + [\#5](https://github.com/zzybluebell/ped/issues/5), + [\#4](https://github.com/zzybluebell/ped/issues/4), + [\#3](https://github.com/zzybluebell/ped/issues/3), + [\#2](https://github.com/zzybluebell/ped/issues/1), + [\#1](https://github.com/zzybluebell/ped/issues/1) diff --git a/src/main/java/seedu/address/AppParameters.java b/src/main/java/seedu/address/AppParameters.java index ab552c398f3..c17de110d96 100644 --- a/src/main/java/seedu/address/AppParameters.java +++ b/src/main/java/seedu/address/AppParameters.java @@ -17,6 +17,7 @@ public class AppParameters { private static final Logger logger = LogsCenter.getLogger(AppParameters.class); private Path configPath; + private Path accountPath; public Path getConfigPath() { return configPath; @@ -26,6 +27,14 @@ public void setConfigPath(Path configPath) { this.configPath = configPath; } + public Path getAccountPath() { + return accountPath; + } + + public void setAccountPath(Path accountPath) { + this.accountPath = accountPath; + } + /** * Parses the application command-line parameters. */ @@ -40,9 +49,19 @@ public static AppParameters parse(Application.Parameters parameters) { } appParameters.setConfigPath(configPathParameter != null ? Paths.get(configPathParameter) : null); + String accountPathParameter = namedParameters.get("account"); + if (accountPathParameter != null && !FileUtil.isValidPath(accountPathParameter)) { + logger.warning("Invalid account path " + accountPathParameter + ". Using default account path."); + accountPathParameter = null; + } + appParameters.setAccountPath(accountPathParameter != null ? Paths.get(accountPathParameter) : null); + return appParameters; } + /** + * Overrides the equals method. + */ @Override public boolean equals(Object other) { if (other == this) { @@ -54,11 +73,15 @@ public boolean equals(Object other) { } AppParameters otherAppParameters = (AppParameters) other; - return Objects.equals(getConfigPath(), otherAppParameters.getConfigPath()); + return Objects.equals(getConfigPath(), otherAppParameters.getConfigPath()) + && Objects.equals(getAccountPath(), otherAppParameters.getAccountPath()); } + /** + * Overrides the hashCode method. + */ @Override public int hashCode() { - return configPath.hashCode(); + return Objects.hash(configPath, accountPath); } } diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/address/Main.java index 052a5068631..4c05610a5d0 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/seedu/address/Main.java @@ -19,6 +19,9 @@ * to be the entry point of the application, we avoid this issue. */ public class Main { + /** + * Starts main application. + */ public static void main(String[] args) { Application.launch(MainApp.class, args); } diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java index 4133aaa0151..06466b70171 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/address/MainApp.java @@ -15,15 +15,19 @@ import seedu.address.commons.util.StringUtil; import seedu.address.logic.Logic; import seedu.address.logic.LogicManager; -import seedu.address.model.AddressBook; +import seedu.address.model.Account; +import seedu.address.model.EzFoodie; import seedu.address.model.Model; import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyAccount; +import seedu.address.model.ReadOnlyEzFoodie; import seedu.address.model.ReadOnlyUserPrefs; import seedu.address.model.UserPrefs; import seedu.address.model.util.SampleDataUtil; -import seedu.address.storage.AddressBookStorage; -import seedu.address.storage.JsonAddressBookStorage; +import seedu.address.storage.AccountStorage; +import seedu.address.storage.EzFoodieStorage; +import seedu.address.storage.JsonAccountStorage; +import seedu.address.storage.JsonEzFoodieStorage; import seedu.address.storage.JsonUserPrefsStorage; import seedu.address.storage.Storage; import seedu.address.storage.StorageManager; @@ -36,6 +40,9 @@ */ public class MainApp extends Application { + /** + * Creates a version. + */ public static final Version VERSION = new Version(0, 2, 0, true); private static final Logger logger = LogsCenter.getLogger(MainApp.class); @@ -46,9 +53,14 @@ public class MainApp extends Application { protected Model model; protected Config config; + /** + * Overrides and initializes rhe ezFoodie application. + * + * @throws Exception if the user input does not conform the expected format. + */ @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("==============================[ Initializing ezFoodie ]============================="); super.init(); AppParameters appParameters = AppParameters.parse(getParameters()); @@ -56,8 +68,9 @@ public void init() throws Exception { UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); UserPrefs userPrefs = initPrefs(userPrefsStorage); - AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath()); - storage = new StorageManager(addressBookStorage, userPrefsStorage); + AccountStorage accountStorage = new JsonAccountStorage(userPrefs.getAccountFilePath()); + EzFoodieStorage ezFoodieStorage = new JsonEzFoodieStorage(userPrefs.getEzFoodieFilePath()); + storage = new StorageManager(accountStorage, ezFoodieStorage, userPrefsStorage); initLogging(config); @@ -69,28 +82,69 @@ public void init() throws Exception { } /** - * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book is not found, - * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book. + * The data from the sample ezFoodie will be used instead if {@code storage}'s account is not found, + * or an empty account will be used instead if errors occur when reading {@code storage}'s account. */ - private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + private ReadOnlyAccount initAccount(Storage storage) { + Optional accountOptional; + ReadOnlyAccount initialData; try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Data file not found. Will be starting with a sample AddressBook"); + accountOptional = storage.readAccount(); + if (!accountOptional.isPresent()) { + logger.info("Account file not found. Will be starting with a sample account"); } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + initialData = accountOptional.orElseGet(SampleDataUtil::getDefaultPassword); } catch (DataConversionException e) { - logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Data file not in the correct format. Will be starting with an empty ezFoodie"); + initialData = new Account(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Problem while reading from the file. Will be starting with an empty ezFoodie"); + initialData = new Account(); } - return new ModelManager(initialData, userPrefs); + //Update account file in case it was missing to begin with or there are any new updates. + try { + storage.saveAccount(initialData); + } catch (IOException e) { + logger.warning("Failed to save account file : " + StringUtil.getDetails(e)); + } + + return initialData; + } + + /** + * The data from the sample ezFoodie will be used instead if {@code storage}'s ezFoodie is not found, + * or an empty ezFoodie will be used instead if errors occur when reading {@code storage}'s ezFoodie. + */ + private ReadOnlyEzFoodie initEzFoodie(Storage storage) { + Optional ezFoodieOptional; + ReadOnlyEzFoodie initialData; + try { + ezFoodieOptional = storage.readEzFoodie(); + if (!ezFoodieOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample ezFoodie"); + } + initialData = ezFoodieOptional.orElseGet(SampleDataUtil::getSampleEzFoodie); + } catch (DataConversionException e) { + logger.warning("Data file not in the correct format. Will be starting with an empty ezFoodie"); + initialData = new EzFoodie(); + } catch (IOException e) { + logger.warning("Problem while reading from the file. Will be starting with an empty ezFoodie"); + initialData = new EzFoodie(); + } + + return initialData; + } + + /** + * Returns a {@code ModelManager} with the data from {@code storage}'s account and ezFoodie + * and {@code userPrefs}. + */ + private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { + ReadOnlyAccount initAccount = initAccount(storage); + ReadOnlyEzFoodie initEzFoodie = initEzFoodie(storage); + + return new ModelManager(initAccount, initEzFoodie, userPrefs); } private void initLogging(Config config) { @@ -151,7 +205,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { + "Using default user prefs"); initializedPrefs = new UserPrefs(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); + logger.warning("Problem while reading from the file. Will be starting with an empty ezFoodie"); initializedPrefs = new UserPrefs(); } @@ -165,15 +219,21 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { return initializedPrefs; } + /** + * Starts main application with input {@code primaryStage}. + */ @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting ezFoodie " + MainApp.VERSION); ui.start(primaryStage); } + /** + * Stops main application. + */ @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("=============================== [ Stopping ezFoodie ] =============================="); try { storage.saveUserPrefs(model.getUserPrefs()); } catch (IOException e) { diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/address/commons/core/Config.java index 91145745521..7d14cd2478e 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/seedu/address/commons/core/Config.java @@ -6,32 +6,51 @@ import java.util.logging.Level; /** - * Config values used by the app + * Config values used by the application */ public class Config { - + /** + * Gets the default config json file. + */ public static final Path DEFAULT_CONFIG_FILE = Paths.get("config.json"); // Config values customizable through config file private Level logLevel = Level.INFO; private Path userPrefsFilePath = Paths.get("preferences.json"); + /** + * Gets log Level from {@code logLevel}. + */ public Level getLogLevel() { return logLevel; } + /** + * Sets log Level from {@code logLevel}. + */ public void setLogLevel(Level logLevel) { this.logLevel = logLevel; } + /** + * Gets User Prefs File Path from {@code userPrefsFilePath}. + * + * @return Path of the user prefs file. + */ public Path getUserPrefsFilePath() { return userPrefsFilePath; } + /** + * Sets User Prefs File Path from {@code userPrefsFilePath}. + */ public void setUserPrefsFilePath(Path userPrefsFilePath) { this.userPrefsFilePath = userPrefsFilePath; } + /** + * Overrides the equals method for Config class. + */ @Override public boolean equals(Object other) { if (other == this) { @@ -47,11 +66,21 @@ public boolean equals(Object other) { && Objects.equals(userPrefsFilePath, o.userPrefsFilePath); } + /** + * Overrides hashcode method for Config class. + * + * @return int objects after hashed with logLevel and userPrefsFilePath. + */ @Override public int hashCode() { return Objects.hash(logLevel, userPrefsFilePath); } + /** + * Overrides toString method for Config class. + * + * @return String the log level and Config file location. + */ @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/address/commons/core/GuiSettings.java index ba33653be67..407b574626d 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/address/commons/core/GuiSettings.java @@ -35,18 +35,36 @@ public GuiSettings(double windowWidth, double windowHeight, int xPosition, int y windowCoordinates = new Point(xPosition, yPosition); } + /** + * Gets GUI window width. + * + * @return double the width of window. + */ public double getWindowWidth() { return windowWidth; } + /** + * Gets GUI window height. + * + * @return double the height of window. + */ public double getWindowHeight() { return windowHeight; } + /** + * Gets GUI window coordinates. + * + * @return Point object of window coordinate. + */ public Point getWindowCoordinates() { return windowCoordinates != null ? new Point(windowCoordinates) : null; } + /** + * Overrides the equals method for GuiSettings. + */ @Override public boolean equals(Object other) { if (other == this) { @@ -63,11 +81,21 @@ public boolean equals(Object other) { && Objects.equals(windowCoordinates, o.windowCoordinates); } + /** + * Overrides the hashCode method. + * + * @return int objects after hashed with windowWidth, windowHeight and windowCoordinates. + */ @Override public int hashCode() { return Objects.hash(windowWidth, windowHeight, windowCoordinates); } + /** + * Overrides the toString Method for GuiSettings. + * + * @return String including all width, height and Position. + */ @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/address/commons/core/LogsCenter.java index 431e7185e76..bff1eeb19cb 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/seedu/address/commons/core/LogsCenter.java @@ -18,7 +18,7 @@ public class LogsCenter { private static final int MAX_FILE_COUNT = 5; private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB - private static final String LOG_FILE = "addressbook.log"; + private static final String LOG_FILE = "ezfoodie.log"; private static Level currentLogLevel = Level.INFO; private static final Logger logger = LogsCenter.getLogger(LogsCenter.class); private static FileHandler fileHandler; @@ -50,7 +50,7 @@ public static Logger getLogger(String name) { } /** - * Creates a Logger for the given class name. + * Creates a logger for the given class name. */ public static Logger getLogger(Class clazz) { if (clazz == null) { @@ -71,7 +71,7 @@ private static void addConsoleHandler(Logger logger) { } /** - * Remove all the handlers from {@code logger}. + * Removes all the handlers from {@code logger}. */ private static void removeHandlers(Logger logger) { Arrays.stream(logger.getHandlers()) @@ -95,6 +95,7 @@ private static void addFileHandler(Logger logger) { /** * Creates a {@code FileHandler} for the log file. + * * @throws IOException if there are problems opening the file. */ private static FileHandler createFileHandler() throws IOException { diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java index 1deb3a1e469..3fab7eafc22 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/seedu/address/commons/core/Messages.java @@ -1,13 +1,50 @@ package seedu.address.commons.core; +import seedu.address.commons.status.LoginStatus; + /** * Container for user visible messages. */ public class Messages { + /** + * Represents the MESSAGE_UNKNOWN_COMMAND. + */ + public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command."; - public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; + /** + * Represents the MESSAGE_INVALID_COMMAND_FORMAT. + */ 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!"; + + /** + * Represents the MESSAGE_INVALID_MEMBER_DISPLAYED_INDEX. + */ + public static final String MESSAGE_INVALID_MEMBER_DISPLAYED_INDEX = "The member index provided is invalid."; + + /** + * Represents the MESSAGE_INVALID_MEMBER_DISPLAYED_ID. + */ + public static final String MESSAGE_INVALID_MEMBER_DISPLAYED_ID = "The member ID provided is invalid."; + + /** + * Represents the MESSAGE_INVALID_TRANSACTION_DISPLAYED_ID. + */ + public static final String MESSAGE_INVALID_TRANSACTION_DISPLAYED_ID = "The transaction ID provided is invalid."; + + /** + * Represents the MESSAGE_INVALID_RESERVATION_DISPLAYED_ID. + */ + public static final String MESSAGE_INVALID_RESERVATION_DISPLAYED_ID = "The reservation ID provided is invalid."; + + /** + * Represents the MESSAGE_MEMBERS_LISTED_OVERVIEW. + */ + public static final String MESSAGE_MEMBERS_LISTED_OVERVIEW = "%1$d members listed!"; + + /** + * Represents the MESSAGE_PERMISSION_DENIED. + */ + public static final String MESSAGE_PERMISSION_DENIED = + "Permission denied! Please login as " + LoginStatus.MANAGER + "."; } diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/address/commons/core/Version.java index 12142ec1e32..f7aedbde324 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/seedu/address/commons/core/Version.java @@ -7,10 +7,13 @@ import com.fasterxml.jackson.annotation.JsonValue; /** - * Represents a version with major, minor and patch number + * Represents a version with major, minor and patch number. */ public class Version implements Comparable { + /** + * Sets the version regex. + */ public static final String VERSION_REGEX = "V(\\d+)\\.(\\d+)\\.(\\d+)(ea)?"; private static final String EXCEPTION_STRING_NOT_VERSION = "String is not a valid Version. %s"; @@ -32,26 +35,47 @@ public Version(int major, int minor, int patch, boolean isEarlyAccess) { this.isEarlyAccess = isEarlyAccess; } + /** + * Gets the major from {@code major}. + * + * @return int of the major. + */ public int getMajor() { return major; } + /** + * Gets the minor from {@code minor}. + * + * @return int of the minor. + */ public int getMinor() { return minor; } + /** + * Gets the patch from {@code patch}. + * + * @return int of the patch. + */ public int getPatch() { return patch; } + /** + * Returns whether is early access. + * + * @return boolean ture is early access. + */ public boolean isEarlyAccess() { return isEarlyAccess; } /** * Parses a version number string in the format V1.2.3. - * @param versionString version number string - * @return a Version object + * + * @param versionString version number string. + * @return a version object. */ @JsonCreator public static Version fromString(String versionString) throws IllegalArgumentException { @@ -67,11 +91,19 @@ public static Version fromString(String versionString) throws IllegalArgumentExc versionMatcher.group(4) == null ? false : true); } + /** + * Overrides the toString for Version class. + * + * @return String for version. + */ @JsonValue public String toString() { return String.format("V%d.%d.%d%s", major, minor, patch, isEarlyAccess ? "ea" : ""); } + /** + * Overrides the compareTo method for Version class. + */ @Override public int compareTo(Version other) { if (major != other.major) { @@ -92,6 +124,9 @@ public int compareTo(Version other) { return 1; } + /** + * Overrides the equals method for version class. + */ @Override public boolean equals(Object obj) { if (obj == null) { @@ -105,6 +140,9 @@ public boolean equals(Object obj) { return compareTo(other) == 0; } + /** + * Overrides the hashCode for version class. + */ @Override public int hashCode() { String hash = String.format("%03d%03d%03d", major, minor, patch); diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/seedu/address/commons/core/index/Index.java index 19536439c09..14ae4fee53e 100644 --- a/src/main/java/seedu/address/commons/core/index/Index.java +++ b/src/main/java/seedu/address/commons/core/index/Index.java @@ -6,7 +6,7 @@ * {@code Index} should be used right from the start (when parsing in a new user input), so that if the current * component wants to communicate with another component, it can send an {@code Index} to avoid having to know what * base the other component is using for its index. However, after receiving the {@code Index}, that component can - * convert it back to an int if the index will not be passed to a different component again. + * convert it back to an integer if the index will not be passed to a different component again. */ public class Index { private int zeroBasedIndex; @@ -23,28 +23,45 @@ private Index(int zeroBasedIndex) { this.zeroBasedIndex = zeroBasedIndex; } + /** + * Gets zero based int. + * + * @return int of zero based index. + */ public int getZeroBased() { return zeroBasedIndex; } + /** + * Gets one based int. + * + * @return int of one based index. + */ public int getOneBased() { return zeroBasedIndex + 1; } /** - * Creates a new {@code Index} using a zero-based index. + * Creates a new {@code Index} using a {@code zeroBasedIndex}. + * + * @return Index of zero based index. */ public static Index fromZeroBased(int zeroBasedIndex) { return new Index(zeroBasedIndex); } /** - * Creates a new {@code Index} using a one-based index. + * Creates a new {@code Index} using an {@code oneBasedIndex}. + * + * @return Index of one based index. */ public static Index fromOneBased(int oneBasedIndex) { return new Index(oneBasedIndex - 1); } + /** + * Overrides the equals method. + */ @Override public boolean equals(Object other) { return other == this // short circuit if same object diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/seedu/address/commons/exceptions/DataConversionException.java index 1f689bd8e3f..fca7e6f5cdd 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/seedu/address/commons/exceptions/DataConversionException.java @@ -1,7 +1,7 @@ package seedu.address.commons.exceptions; /** - * Represents an error during conversion of data from one format to another + * Represents an error during conversion of data from one format to another. */ public class DataConversionException extends Exception { public DataConversionException(Exception cause) { diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java index 19124db485c..ecfd1d1db84 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java @@ -5,15 +5,15 @@ */ public class IllegalValueException extends Exception { /** - * @param message should contain relevant information on the failed constraint(s) + * @param message should contain relevant information on the failed constraint(s). */ public IllegalValueException(String message) { super(message); } /** - * @param message should contain relevant information on the failed constraint(s) - * @param cause of the main exception + * @param message should contain relevant information on the failed constraint(s). + * @param cause of the main exception. */ public IllegalValueException(String message, Throwable cause) { super(message, cause); diff --git a/src/main/java/seedu/address/commons/exceptions/PermissionException.java b/src/main/java/seedu/address/commons/exceptions/PermissionException.java new file mode 100644 index 00000000000..a8788cfd9a9 --- /dev/null +++ b/src/main/java/seedu/address/commons/exceptions/PermissionException.java @@ -0,0 +1,15 @@ +package seedu.address.commons.exceptions; + +/** + * Represents an error during insufficient permission. + */ +public class PermissionException extends Exception { + + /** + * @param message should contain relevant information on the failed constraint(s). + */ + public PermissionException(String message) { + super(message); + } + +} diff --git a/src/main/java/seedu/address/commons/status/ExecutionStatus.java b/src/main/java/seedu/address/commons/status/ExecutionStatus.java new file mode 100644 index 00000000000..31c3c2f5240 --- /dev/null +++ b/src/main/java/seedu/address/commons/status/ExecutionStatus.java @@ -0,0 +1,8 @@ +package seedu.address.commons.status; + +/** + * Represents the execution status. + */ +public enum ExecutionStatus { + NORMAL, TEST +} diff --git a/src/main/java/seedu/address/commons/status/LoginStatus.java b/src/main/java/seedu/address/commons/status/LoginStatus.java new file mode 100644 index 00000000000..4d6856a28b5 --- /dev/null +++ b/src/main/java/seedu/address/commons/status/LoginStatus.java @@ -0,0 +1,46 @@ +package seedu.address.commons.status; + +import javafx.beans.property.SimpleStringProperty; + +/** + * Represents the login status. + */ +public enum LoginStatus { + + STAFF("STAFF"), + MANAGER("MANAGER"); + + /** + * Represents the static the simple string property. + */ + public static final SimpleStringProperty CURRENT_STATUS = new SimpleStringProperty(STAFF.value); + + private final String value; + + /** + * Constructs {@code LoginStatus} with the given value. + */ + LoginStatus(String value) { + this.value = value; + } + + /** + * Gets login status. + * + * Returns {@code LoginStatus} the status of login. + */ + public static LoginStatus getLoginStatus() { + if (CURRENT_STATUS.getValue().equals(STAFF.value)) { + return STAFF; + } else { + return MANAGER; + } + } + + /** + * Resets {@code CURRENT_STATUS} by {@code LoginStatus}. + */ + public static void setLoginStatus(LoginStatus loginStatus) { + CURRENT_STATUS.setValue(loginStatus.value); + } +} diff --git a/src/main/java/seedu/address/commons/status/SortStatus.java b/src/main/java/seedu/address/commons/status/SortStatus.java new file mode 100644 index 00000000000..1eb098668b7 --- /dev/null +++ b/src/main/java/seedu/address/commons/status/SortStatus.java @@ -0,0 +1,8 @@ +package seedu.address.commons.status; + +/** + * Represents the sort status. + */ +public enum SortStatus { + ASC, DESC +} diff --git a/src/main/java/seedu/address/commons/util/CommandUtil.java b/src/main/java/seedu/address/commons/util/CommandUtil.java new file mode 100644 index 00000000000..7942209c683 --- /dev/null +++ b/src/main/java/seedu/address/commons/util/CommandUtil.java @@ -0,0 +1,58 @@ +package seedu.address.commons.util; + +import java.util.ArrayList; +import java.util.List; + +public class CommandUtil { + + private static final int MAX_LENGTH = 30; + private static final List commands = new ArrayList<>(); + + private static int pointer = 0; + + /** + * Adds command to storage so that it can be retrieved. + * + * @param command to be added. + */ + public static void addCommand(String command) { + if (!commands.isEmpty() && commands.size() >= MAX_LENGTH) { + commands.remove(0); + } + commands.add(command); + pointer = commands.size(); + } + + /** + * Gets previous command from storage. + */ + public static String getPreCommand() { + if (pointer > 0) { + pointer--; + return commands.get(pointer); + } else { + pointer = -1; + return ""; + } + } + + /** + * Gets next command from storage. + */ + public static String getNextCommand() { + if (pointer < commands.size() - 1) { + pointer++; + return commands.get(pointer); + } else { + pointer = commands.size(); + return ""; + } + } + + /** + * Gets size of commands that stored in storage. + */ + public static int getSize() { + return commands.size(); + } +} diff --git a/src/main/java/seedu/address/commons/util/DateTimeUtil.java b/src/main/java/seedu/address/commons/util/DateTimeUtil.java new file mode 100644 index 00000000000..622595025c3 --- /dev/null +++ b/src/main/java/seedu/address/commons/util/DateTimeUtil.java @@ -0,0 +1,62 @@ +package seedu.address.commons.util; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.Date; + +/** + * Helper functions for handling dates and times. + */ +public class DateTimeUtil { + + public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm"; + + private static final long ONE_DAY_MILLISECONDS = 24 * 60 * 60 * 1000; + private static final String TIMESTAMP_STUB = "1609459200000"; + + /** + * Returns current timestamp in string format. + */ + public static String generateTimestamp() { + return String.valueOf(System.currentTimeMillis()); + } + + /** + * Returns timestamp stub in string format. + */ + public static String generateTimestampStub() { + return TIMESTAMP_STUB; + } + + /** + * Returns true if the {@code date} contains the {@code timestamp}. + */ + public static boolean isDateContainsTimestamp(Date date, long timestamp) { + Timestamp dateToTimestamp = new Timestamp(date.getTime()); + long start = dateToTimestamp.getTime(); + long end = dateToTimestamp.getTime() + ONE_DAY_MILLISECONDS; + return timestamp >= start && timestamp < end; + } + + /** + * Returns date converted by {@code timestamp}. + */ + public static Date timestampToDate(long timestamp) { + Timestamp stamp = new Timestamp(timestamp); + return new Date(stamp.getTime()); + } + + /** + * Returns String of dateTime to dateTime format. + * + * @param dateTime DateTime in String format. + * @return DateTime format. + * @throws DateTimeParseException If the dateTime cannot be parsed. + */ + public static LocalDateTime parseDateTime(String dateTime) throws DateTimeParseException { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN); + return LocalDateTime.parse(dateTime, formatter); + } +} diff --git a/src/main/java/seedu/address/commons/util/EncryptUtil.java b/src/main/java/seedu/address/commons/util/EncryptUtil.java new file mode 100644 index 00000000000..7e34dcb55f8 --- /dev/null +++ b/src/main/java/seedu/address/commons/util/EncryptUtil.java @@ -0,0 +1,30 @@ +package seedu.address.commons.util; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * A class for accessing the Account File. + */ +public class EncryptUtil { + + private static final String HASH_ALGORITHM = "MD5"; + private static final String HASH_FORMAT = "%032x"; + private static final int SIGNUM = 1; + + /** + * Returns hashed text from plaintext. + * + * @throws NoSuchAlgorithmException if the algorithm does not exist. + */ + public static String hash(String plaintext) throws NoSuchAlgorithmException { + byte[] bytesOfPassword = plaintext.getBytes(StandardCharsets.UTF_8); + MessageDigest messageDigest = MessageDigest.getInstance(HASH_ALGORITHM); + messageDigest.reset(); + messageDigest.update(bytesOfPassword); + byte[] digest = messageDigest.digest(); + return String.format(HASH_FORMAT, new BigInteger(SIGNUM, digest)); + } +} diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java index 92cd8fa605a..cba32c1405e 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/address/logic/Logic.java @@ -4,11 +4,12 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; +import seedu.address.commons.exceptions.PermissionException; import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.ReadOnlyEzFoodie; +import seedu.address.model.member.Member; /** * API of the Logic component @@ -16,27 +17,37 @@ public interface Logic { /** * Executes the command and returns the result. + * * @param commandText The command as entered by the user. * @return the result of the command execution. - * @throws CommandException If an error occurs during command execution. - * @throws ParseException If an error occurs during parsing. + * @throws CommandException If an error occurs during command execution. + * @throws ParseException If an error occurs during parsing. + * @throws PermissionException If an error occurs during insufficient permission. */ - CommandResult execute(String commandText) throws CommandException, ParseException; + CommandResult execute(String commandText) throws CommandException, ParseException, PermissionException; /** - * Returns the AddressBook. + * Returns the EzFoodie. * - * @see seedu.address.model.Model#getAddressBook() + * @see seedu.address.model.Model#getEzFoodie() + */ + ReadOnlyEzFoodie getEzFoodie(); + + /** + * Returns an unmodifiable view of the sorted or filtered list of members */ - ReadOnlyAddressBook getAddressBook(); + ObservableList getUpdatedMemberList(); - /** Returns an unmodifiable view of the filtered list of persons */ - ObservableList getFilteredPersonList(); + /** + * Returns an unmodifiable view of the sorted or filtered list of members + * for viewCommand to use only + */ + ObservableList getUpdatedMemberListForView(); /** - * Returns the user prefs' address book file path. + * Returns the user prefs' ezFoodie file path. */ - Path getAddressBookFilePath(); + Path getEzFoodieFilePath(); /** * Returns the user prefs' GUI settings. diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 9d9c6d15bdc..6e8fae413c6 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -7,18 +7,20 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.exceptions.PermissionException; +import seedu.address.commons.status.ExecutionStatus; import seedu.address.logic.commands.Command; import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.AddressBookParser; +import seedu.address.logic.parser.EzFoodieParser; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.ReadOnlyEzFoodie; +import seedu.address.model.member.Member; import seedu.address.storage.Storage; /** - * The main LogicManager of the app. + * The main LogicManager of the application. */ public class LogicManager implements Logic { public static final String FILE_OPS_ERROR_MESSAGE = "Could not save data to file: "; @@ -26,7 +28,7 @@ public class LogicManager implements Logic { private final Model model; private final Storage storage; - private final AddressBookParser addressBookParser; + private final EzFoodieParser ezFoodieParser; /** * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. @@ -34,19 +36,38 @@ public class LogicManager implements Logic { public LogicManager(Model model, Storage storage) { this.model = model; this.storage = storage; - addressBookParser = new AddressBookParser(); + ezFoodieParser = new EzFoodieParser(model); } + /** + * Constructs a {@code LogicManager} with the given {@code Model}, {@code Storage} and {@code ExecutionStatus}. + */ + public LogicManager(Model model, Storage storage, ExecutionStatus executionStatus) { + this.model = model; + this.storage = storage; + ezFoodieParser = new EzFoodieParser(model, executionStatus); + } + + /** + * Executes with given string of command text. + * + * @param commandText The command as entered by the user. + * @return {@code CommandResult} related to logic Manager. + * @throws CommandException if the user input does not conform the expected format. + * @throws ParseException if the user input does not conform the expected format. + * @throws PermissionException if the user input does not conform the expected format. + */ @Override - public CommandResult execute(String commandText) throws CommandException, ParseException { + public CommandResult execute(String commandText) throws CommandException, ParseException, PermissionException { logger.info("----------------[USER COMMAND][" + commandText + "]"); CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); + Command command = ezFoodieParser.parseCommand(commandText); commandResult = command.execute(model); try { - storage.saveAddressBook(model.getAddressBook()); + storage.saveEzFoodie(model.getEzFoodie()); + storage.saveAccount(model.getAccount()); } catch (IOException ioe) { throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); } @@ -54,26 +75,49 @@ public CommandResult execute(String commandText) throws CommandException, ParseE return commandResult; } + /** + * Gets EzFoodie. + */ @Override - public ReadOnlyAddressBook getAddressBook() { - return model.getAddressBook(); + public ReadOnlyEzFoodie getEzFoodie() { + return model.getEzFoodie(); } + /** + * Gets updated member list. + */ @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); + public ObservableList getUpdatedMemberList() { + return model.getUpdatedMemberList(); } + /** + * Gets updated member list for view. + */ @Override - public Path getAddressBookFilePath() { - return model.getAddressBookFilePath(); + public ObservableList getUpdatedMemberListForView () { + return model.getUpdatedMemberListForView(); } + /** + * Gets EzFoodie Path. + */ + @Override + public Path getEzFoodieFilePath() { + return model.getEzFoodieFilePath(); + } + + /** + * Gets Gui Settings. + */ @Override public GuiSettings getGuiSettings() { return model.getGuiSettings(); } + /** + * Sets Gui settings. + */ @Override public void setGuiSettings(GuiSettings guiSettings) { model.setGuiSettings(guiSettings); diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java index 71656d7c5c8..d986d5296ef 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddCommand.java @@ -1,67 +1,40 @@ 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_TAG; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESERVATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TRANSACTION; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Person; /** - * Adds a person to the address book. + * Represents a add command to add member or transaction to ezFoodie. */ -public class AddCommand extends Command { +public abstract class AddCommand extends Command { + /** + * Stands for COMMAND WORD for add command. + */ public static final String COMMAND_WORD = "add"; - 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" - + "Example: " + COMMAND_WORD + " " - + PREFIX_NAME + "John Doe " - + 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"; - - private final Person toAdd; - /** - * Creates an AddCommand to add the specified {@code Person} + * Stands for output message. */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; - } + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Adds a member or a transaction or a reservation to the ezFoodie.\n" + + "With " + + PREFIX_MEMBER + " (member details) or " + + PREFIX_TRANSACTION + " (transaction details) or " + + PREFIX_RESERVATION + " (reservation details)"; + /** + * Executes method for addCommand class to execute model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult for execute addCommand. + * @throws CommandException if the user input does not conform the expected format. + */ @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } + public abstract CommandResult execute(Model model) throws CommandException; - model.addPerson(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddCommand // instanceof handles nulls - && toAdd.equals(((AddCommand) other).toAdd)); - } } diff --git a/src/main/java/seedu/address/logic/commands/AddMemberCommand.java b/src/main/java/seedu/address/logic/commands/AddMemberCommand.java new file mode 100644 index 00000000000..c1dfa5f05c6 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddMemberCommand.java @@ -0,0 +1,82 @@ +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_MEMBER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.member.Id; +import seedu.address.model.member.Member; + +/** + * Adds a member to the ezFoodie. + */ +public class AddMemberCommand extends AddCommand { + + /** + * Stands for command word for add member. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a member to the ezFoodie.\n" + + "Parameters:\n" + + PREFIX_MEMBER + " " + + PREFIX_NAME + "NAME " + + PREFIX_PHONE + "PHONE " + + PREFIX_EMAIL + "EMAIL " + + PREFIX_ADDRESS + "ADDRESS\n" + + "Example:\n" + + COMMAND_WORD + " " + + PREFIX_MEMBER + " " + + PREFIX_NAME + "John Doe " + + PREFIX_PHONE + "98765432 " + + PREFIX_EMAIL + "johnd@example.com " + + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25"; + + public static final String MESSAGE_SUCCESS = "New member added: %1$s"; + public static final String MESSAGE_FULL = "Member ID has reached " + Id.MAX + "."; + public static final String MESSAGE_DUPLICATE_MEMBER = + "This member (phone or email) already exists in the ezFoodie."; + + private final Member toAdd; + + /** + * Constructs an {@codeAddMemberCommand} to add the specified {@code Member}. + */ + public AddMemberCommand(Member member) { + requireNonNull(member); + toAdd = member; + } + + /** + * Executes model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult with toAdd member. + * @throws CommandException if the user input does not conform the expected format. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasMember(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_MEMBER); + } + + model.addMember(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddMemberCommand // instanceof handles nulls + && toAdd.equals(((AddMemberCommand) other).toAdd)); + } + +} diff --git a/src/main/java/seedu/address/logic/commands/AddReservationCommand.java b/src/main/java/seedu/address/logic/commands/AddReservationCommand.java new file mode 100644 index 00000000000..0531e5c7ed9 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddReservationCommand.java @@ -0,0 +1,145 @@ +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_DATE_TIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REMARK; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESERVATION; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_MEMBERS; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.util.DateTimeUtil; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.Timestamp; +import seedu.address.model.member.Address; +import seedu.address.model.member.Credit; +import seedu.address.model.member.Email; +import seedu.address.model.member.Member; +import seedu.address.model.member.Name; +import seedu.address.model.member.Phone; +import seedu.address.model.member.Point; +import seedu.address.model.reservation.Reservation; +import seedu.address.model.tag.Tag; +import seedu.address.model.transaction.Transaction; + +/** + * Adds a reservation to the ezFoodie. + */ +public class AddReservationCommand extends AddCommand { + + /** + * Stands for the message add reservation command. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds reservation to member " + + "by member ID in the ezFoodie.\n" + + "Parameters:\n" + + PREFIX_RESERVATION + " " + + PREFIX_DATE_TIME + "DATE_TIME (" + DateTimeUtil.DATE_TIME_PATTERN + ") " + + PREFIX_REMARK + "REMARK " + + PREFIX_ID + "ID\n" + + "Example:\n" + + COMMAND_WORD + " " + + PREFIX_RESERVATION + " " + + PREFIX_DATE_TIME + "2021-12-01 13:00 " + + PREFIX_REMARK + "2 people " + + PREFIX_ID + "00001"; + + /** + * Stands for message success for new reservation added. + */ + public static final String MESSAGE_SUCCESS = "New reservation added: %1$s"; + public static final String MESSAGE_FULL = "Reservation ID has reached " + seedu.address.model.reservation.Id.MAX; + public static final String MESSAGE_SAME_DATE = "Only one reservation can be added within the same day. " + + "Previous reservation: %1$s"; + + private final Reservation reservationToAdd; + private final seedu.address.model.member.Id idToAdd; + + /** + * Constructs an {@code AddReservationCommand} to add the specified {@code Member}. + */ + public AddReservationCommand(Reservation reservation, seedu.address.model.member.Id id) { + requireAllNonNull(reservation, id); + reservationToAdd = reservation; + idToAdd = id; + } + + /** + * Executes the model in AddReservationCommand. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult with edited member. + * @throws CommandException if the user input does not conform the expected format. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getUpdatedMemberList(); + Member memberToEdit = lastShownList.stream() + .filter(member -> idToAdd.equals(member.getId())).findAny().orElse(null); + if (memberToEdit == null) { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_ID); + } + if (!Reservation.isValidDateTime(reservationToAdd.getDateTime())) { + throw new CommandException(Reservation.MESSAGE_CONSTRAINTS); + } + Reservation reservationSameDate = memberToEdit.getReservations().stream() + .filter(reservation -> reservation.isSameDate(reservationToAdd)).findAny().orElse(null); + if (reservationSameDate == null) { + Member editedMember = createEditedMember(memberToEdit, reservationToAdd); + model.setMember(memberToEdit, editedMember); + model.updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS); + return new CommandResult(String.format(MESSAGE_SUCCESS, "Id: " + editedMember.getId() + + "; Name: " + editedMember.getName() + + "; Reservation: " + "[" + reservationToAdd + "]")); + } + throw new CommandException(String.format(MESSAGE_SAME_DATE, reservationSameDate)); + } + + /** + * Creates and returns a {@code Member} with the details of {@code memberToEdit} and {@code reservationToAdd}. + * + * @param memberToEdit {@code memberToEdit} which the command should operate on. + * @param reservationToAdd {@code reservation} which the command should operate on. + * @return Member with updated reservations. + */ + private static Member createEditedMember(Member memberToEdit, Reservation reservationToAdd) { + assert memberToEdit != null; + assert reservationToAdd != null; + + seedu.address.model.member.Id id = memberToEdit.getId(); + Name name = memberToEdit.getName(); + Phone phone = memberToEdit.getPhone(); + Email email = memberToEdit.getEmail(); + Address address = memberToEdit.getAddress(); + Timestamp timestamp = memberToEdit.getTimestamp(); + Credit credit = memberToEdit.getCredit(); + Point point = memberToEdit.getPoint(); + List transactions = memberToEdit.getTransactions(); + List reservations = memberToEdit.getReservations(); + Set tags = memberToEdit.getTags(); + + List updatedReservations = new ArrayList<>(reservations); + updatedReservations.add(reservationToAdd); + + return new Member(id, name, phone, email, address, timestamp, credit, point, + transactions, updatedReservations, tags); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddReservationCommand // instanceof handles nulls + && reservationToAdd.equals(((AddReservationCommand) other).reservationToAdd)) + && idToAdd.equals(((AddReservationCommand) other).idToAdd); + } +} diff --git a/src/main/java/seedu/address/logic/commands/AddTransactionCommand.java b/src/main/java/seedu/address/logic/commands/AddTransactionCommand.java new file mode 100644 index 00000000000..a5e6e356888 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddTransactionCommand.java @@ -0,0 +1,131 @@ +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_BILLING; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TRANSACTION; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_MEMBERS; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.Timestamp; +import seedu.address.model.member.Address; +import seedu.address.model.member.Credit; +import seedu.address.model.member.Email; +import seedu.address.model.member.Member; +import seedu.address.model.member.Name; +import seedu.address.model.member.Phone; +import seedu.address.model.member.Point; +import seedu.address.model.reservation.Reservation; +import seedu.address.model.tag.Tag; +import seedu.address.model.transaction.Transaction; + +/** + * Adds a transaction to the ezFoodie. + */ +public class AddTransactionCommand extends AddCommand { + + /** + * Stands for the message add transaction command. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a transaction to each member in the ezFoodie.\n" + + "Parameters:\n" + + PREFIX_TRANSACTION + " " + + PREFIX_BILLING + "BILLING_AMOUNT (STRICTLY 2 DECIMAL PLACES) " + + PREFIX_ID + "ID\n" + + "Example:\n" + + COMMAND_WORD + " " + + PREFIX_TRANSACTION + " " + + PREFIX_BILLING + "23.00 " + + PREFIX_ID + "00001"; + + /** + * Stands for the success message of new transaction added. + */ + public static final String MESSAGE_SUCCESS = "New transaction added: %1$s"; + public static final String MESSAGE_FULL = "Transaction ID has reached " + seedu.address.model.transaction.Id.MAX; + + private final Transaction transactionToAdd; + private final seedu.address.model.member.Id idToAdd; + + /** + * Constructs an AddTransactionCommand to add the specified {@code Member}. + */ + public AddTransactionCommand(Transaction transaction, seedu.address.model.member.Id id) { + requireAllNonNull(transaction, id); + transactionToAdd = transaction; + idToAdd = id; + } + + /** + * Executes the model in add transaction command. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult with edited member. + * @throws CommandException if the user input does not conform the expected format. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getUpdatedMemberList(); + Member memberToEdit = lastShownList.stream() + .filter(member -> idToAdd.equals(member.getId())).findAny().orElse(null); + if (memberToEdit == null) { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_ID); + } + Member editedMember = createEditedMember(memberToEdit, transactionToAdd); + model.setMember(memberToEdit, editedMember); + model.updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS); + return new CommandResult(String.format(MESSAGE_SUCCESS, "Id: " + editedMember.getId() + + "; Name: " + editedMember.getName() + + "; Transaction: " + "[" + transactionToAdd + "]")); + } + + /** + * Creates and returns a {@code Member} with the details of {@code memberToEdit} and {@code transactionToAdd}. + * + * @param memberToEdit {@code memberToEdit} which the command should operate on. + * @param transactionToAdd {@code transaction} which the command should operate on. + * @return member with updated transactions and points. + */ + private static Member createEditedMember(Member memberToEdit, Transaction transactionToAdd) { + assert memberToEdit != null; + assert transactionToAdd != null; + + seedu.address.model.member.Id id = memberToEdit.getId(); + Name name = memberToEdit.getName(); + Phone phone = memberToEdit.getPhone(); + Email email = memberToEdit.getEmail(); + Address address = memberToEdit.getAddress(); + Timestamp timestamp = memberToEdit.getTimestamp(); + List transactions = memberToEdit.getTransactions(); + List reservations = memberToEdit.getReservations(); + Set tags = memberToEdit.getTags(); + List updatedTransactions = new ArrayList<>(transactions); + updatedTransactions.add(transactionToAdd); + Credit updatedCredit = new Credit("" + Math.min(updatedTransactions.stream() + .mapToInt(t -> (int) t.getBilling().getDoubleValue()).sum(), Credit.MAX)); + Point updatePoint = new Point("" + Math.min(Integer.parseInt(String.valueOf(updatedCredit.getIntValue() + - memberToEdit.getCredit().getIntValue() + + memberToEdit.getPoint().getIntValue())), Point.MAX)); + return new Member(id, name, phone, email, address, timestamp, updatedCredit, + updatePoint, updatedTransactions, reservations, tags); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddTransactionCommand // instanceof handles nulls + && transactionToAdd.equals(((AddTransactionCommand) other).transactionToAdd)) + && idToAdd.equals(((AddTransactionCommand) other).idToAdd); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java index 9c86b1fa6e4..2ef9a53adb7 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java @@ -2,22 +2,34 @@ import static java.util.Objects.requireNonNull; -import seedu.address.model.AddressBook; +import seedu.address.model.EzFoodie; import seedu.address.model.Model; /** - * Clears the address book. + * Clears the ezFoodie. */ public class ClearCommand extends Command { + /** + * Stands for clean command word. + */ public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; + /** + * Stands for success message for clear command. + */ + public static final String MESSAGE_SUCCESS = "ezFoodie has been cleared!"; + + /** + * Executes the model in the clear command. + * + * @param model {@code Model} which the command should operate on. + */ @Override public CommandResult execute(Model model) { requireNonNull(model); - model.setAddressBook(new AddressBook()); + model.setEzFoodie(new EzFoodie()); return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/address/logic/commands/Command.java index 64f18992160..68b9f092d60 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/address/logic/commands/Command.java @@ -12,7 +12,7 @@ public abstract class Command { * Executes the command and returns the result message. * * @param model {@code Model} which the command should operate on. - * @return feedback message of the operation result for display + * @return feedback message of the operation result for display. * @throws CommandException If an error occurs during command execution. */ public abstract CommandResult execute(Model model) throws CommandException; diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java index 92f900b7916..143b1d26547 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/address/logic/commands/CommandResult.java @@ -11,19 +11,38 @@ public class CommandResult { private final String feedbackToUser; - /** Help information should be shown to the user. */ + /** + * Help information should be shown to the user. + */ private final boolean showHelp; - /** The application should exit. */ + /** + * Member view information should be shown to the user. + */ + private final boolean showMemberView; + + /** + * Summary information should be shown to the user. + */ + private final boolean showSummary; + + /** + * The application should exit. + */ private final boolean exit; /** - * Constructs a {@code CommandResult} with the specified fields. + * Constructs a {@code CommandResult} with the specified fields from {@code feedbackToUser}, + * {@code showHelp}, {@code exit}, {@code showMemberView} and + * {@codeshowSummary}. */ - public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, boolean showMemberView, + boolean showSummary) { this.feedbackToUser = requireNonNull(feedbackToUser); this.showHelp = showHelp; + this.showMemberView = showMemberView; this.exit = exit; + this.showSummary = showSummary; } /** @@ -31,21 +50,57 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { * and other fields set to their default value. */ public CommandResult(String feedbackToUser) { - this(feedbackToUser, false, false); + this(feedbackToUser, false, false, false, false); } + /** + * Gets feedback to user. + * + * @return string for feedback to user. + */ public String getFeedbackToUser() { return feedbackToUser; } + /** + * Determines whether the app should show help window. + * + * @return boolean if true is showHelp command. + */ public boolean isShowHelp() { return showHelp; } + /** + * Determines whether the app should show member window. + * + * @return boolean if true is shown member view command. + */ + public boolean isShowMemberView() { + return showMemberView; + } + + /** + * Determines whether the app should show summary window. + * + * @return boolean if true is shown summary command. + */ + public boolean isShowSummary() { + return showSummary; + } + + /** + * Determines whether the app should exit. + * + * @return boolean if ture is exit command. + */ public boolean isExit() { return exit; } + /** + * Overrides the equals method. + */ @Override public boolean equals(Object other) { if (other == this) { @@ -60,12 +115,17 @@ public boolean equals(Object other) { CommandResult otherCommandResult = (CommandResult) other; return feedbackToUser.equals(otherCommandResult.feedbackToUser) && showHelp == otherCommandResult.showHelp - && exit == otherCommandResult.exit; + && showMemberView == otherCommandResult.showMemberView + && exit == otherCommandResult.exit + && showSummary == otherCommandResult.showSummary; } + /** + * Overrides the hashCode method. + */ @Override public int hashCode() { - return Objects.hash(feedbackToUser, showHelp, exit); + return Objects.hash(feedbackToUser, showHelp, showMemberView, exit, showSummary); } } diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java index 02fd256acba..a967910e469 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java @@ -1,53 +1,40 @@ package seedu.address.logic.commands; -import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESERVATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TRANSACTION; -import java.util.List; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Person; /** - * Deletes a person identified using it's displayed index from the address book. + * Deletes a member identified using it's displayed index from the ezFoodie. */ -public class DeleteCommand extends Command { +public abstract class DeleteCommand extends Command { - public static final String COMMAND_WORD = "delete"; + /** + * Stands for delete command. + */ + public static final String COMMAND_WORD = "del"; + /** + * Stands for the message of delete command from the ezFoodie. + */ 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 a member or a transaction or a reservation from the ezFoodie.\n" + + "With " + + PREFIX_MEMBER + " (member details) or " + + PREFIX_TRANSACTION + " (transaction details) or " + + PREFIX_RESERVATION + " (reservation details)"; + + /** + * Overrides and executes model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult of related commands. + * @throws CommandException if the user input does not conform the expected format. + */ @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); - } + public abstract CommandResult execute(Model model) throws CommandException; - Person personToDelete = lastShownList.get(targetIndex.getZeroBased()); - model.deletePerson(personToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof DeleteCommand // instanceof handles nulls - && targetIndex.equals(((DeleteCommand) other).targetIndex)); // state check - } } diff --git a/src/main/java/seedu/address/logic/commands/DeleteMemberCommand.java b/src/main/java/seedu/address/logic/commands/DeleteMemberCommand.java new file mode 100644 index 00000000000..44a4b2a5190 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeleteMemberCommand.java @@ -0,0 +1,113 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.member.Id; +import seedu.address.model.member.Member; + +/** + * Deletes a member identified using it's displayed index or member ID from the ezFoodie. + */ +public class DeleteMemberCommand extends DeleteCommand { + + /** + * Stands for delete command. + */ + public static final String COMMAND_WORD = "del"; + + /** + * Stands for the message of delete command related to a member from the ezFoodie. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the member identified by the index number used in the displayed member list or member ID.\n" + + "Parameters:\n" + + "Delete by index number: " + PREFIX_MEMBER + " " + PREFIX_INDEX + "INDEX" + + " (INDEX must be a positive integer)\n" + + "Delete by member ID: " + PREFIX_MEMBER + " " + PREFIX_ID + "ID\n" + + "Example:\n" + + "Delete by index number: " + COMMAND_WORD + " " + PREFIX_MEMBER + " " + PREFIX_INDEX + "1\n" + + "Delete by member ID: " + COMMAND_WORD + " " + PREFIX_MEMBER + " " + PREFIX_ID + "00001"; + + /** + * Stands for succeed message of delete member. + */ + public static final String MESSAGE_SUCCESS = "Deleted Member: %1$s"; + + private final Index index; + private final Id id; + + /** + * Creates an DeleteCommand to delete the specified {@code Member} by {@code index} number. + * + * @param index the index shown in the page. + */ + public DeleteMemberCommand(Index index) { + requireNonNull(index); + this.index = index; + id = null; + } + + /** + * Creates an DeleteCommand to delete the specified {@code Member} by member ID. + * + * @param id the member ID. + */ + public DeleteMemberCommand(Id id) { + requireNonNull(id); + this.id = id; + index = null; + } + + /** + * Executes the model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related delete member command. + * @throws CommandException if the user input does not conform the expected format. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getUpdatedMemberList(); + + Member memberToDelete = null; + if (index != null) { + if (index.getZeroBased() < lastShownList.size()) { + memberToDelete = lastShownList.get(index.getZeroBased()); + } else { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_INDEX); + } + } + if (id != null) { + memberToDelete = lastShownList.stream() + .filter(member -> id.equals(member.getId())).findAny().orElse(null); + } + if (memberToDelete != null) { + model.deleteMember(memberToDelete); + return new CommandResult(String.format(MESSAGE_SUCCESS, memberToDelete)); + } else { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_ID); + } + + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteMemberCommand // instanceof handles nulls + && (index == null || index.equals(((DeleteMemberCommand) other).index)) + && (id == null || id.equals(((DeleteMemberCommand) other).id))); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/DeleteReservationCommand.java b/src/main/java/seedu/address/logic/commands/DeleteReservationCommand.java new file mode 100644 index 00000000000..f07af26a073 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeleteReservationCommand.java @@ -0,0 +1,141 @@ +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_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESERVATION; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_MEMBERS; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.Timestamp; +import seedu.address.model.member.Address; +import seedu.address.model.member.Credit; +import seedu.address.model.member.Email; +import seedu.address.model.member.Member; +import seedu.address.model.member.Name; +import seedu.address.model.member.Phone; +import seedu.address.model.member.Point; +import seedu.address.model.reservation.Reservation; +import seedu.address.model.tag.Tag; +import seedu.address.model.transaction.Transaction; + +/** + * Deletes a reservation identified by it's member ID and reservation ID from the ezFoodie. + */ +public class DeleteReservationCommand extends DeleteCommand { + + /** + * Stands for delete command. + */ + public static final String COMMAND_WORD = "del"; + + /** + * Stands for the message of delete command related to reservations. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the reservation identified by the member ID and reservation ID.\n" + + "Parameters:\n" + + "Delete by member ID and reservation ID: " + + PREFIX_RESERVATION + " " + PREFIX_ID + "member ID + reservation ID\n" + + "Example:\n" + + "Delete by member ID and reservation ID: " + + COMMAND_WORD + " " + PREFIX_RESERVATION + " " + PREFIX_ID + "00001000001"; + + /** + * Stands for succeed message of delete reservation. + */ + public static final String MESSAGE_SUCCESS = "Deleted reservation: %1$s"; + + private final seedu.address.model.member.Id memberId; + private final seedu.address.model.reservation.Id reservationId; + + /** + * Constructs DeleteReservationCommand to delete the specified {@code Member} + * by {@code memberID} and {@code reservationId}. + * + * @param memberId the member Id + * @param reservationId the reservation id + */ + public DeleteReservationCommand( + seedu.address.model.member.Id memberId, seedu.address.model.reservation.Id reservationId) { + requireAllNonNull(memberId, reservationId); + this.memberId = memberId; + this.reservationId = reservationId; + } + + /** + * Creates and returns a {@code Member} with the details of {@code memberToEdit} and {@code reservationToDelete} + * + * @param memberToEdit the member to edit. + * @param reservationToDelete the reservation will to remove. + * @return Member with updated reservation. + */ + private static Member createEditedMember(Member memberToEdit, Reservation reservationToDelete) { + assert memberToEdit != null; + assert reservationToDelete != null; + + seedu.address.model.member.Id id = memberToEdit.getId(); + Name updatedName = memberToEdit.getName(); + Phone updatedPhone = memberToEdit.getPhone(); + Email updatedEmail = memberToEdit.getEmail(); + Address updatedAddress = memberToEdit.getAddress(); + Timestamp timestamp = memberToEdit.getTimestamp(); + Point point = memberToEdit.getPoint(); + Credit credit = memberToEdit.getCredit(); + List transactions = memberToEdit.getTransactions(); + List reservations = memberToEdit.getReservations(); + Set updatedTags = memberToEdit.getTags(); + + List updatedReservations = new ArrayList<>(reservations); + updatedReservations.remove(reservationToDelete); + + return new Member(id, updatedName, updatedPhone, updatedEmail, updatedAddress, timestamp, credit, point, + transactions, updatedReservations, updatedTags); + } + + /** + * Executes the model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related delete reservation command. + * @throws CommandException if the user input does not conform the expected format. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getUpdatedMemberList(); + Member memberToEdit = lastShownList.stream() + .filter(member -> memberId.equals(member.getId())).findAny().orElse(null); + if (memberToEdit == null) { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_ID); + } + Reservation reservationToDelete = memberToEdit.getReservations().stream() + .filter(reservation -> reservationId.equals(reservation.getId())).findAny().orElse(null); + if (reservationToDelete == null) { + throw new CommandException(Messages.MESSAGE_INVALID_RESERVATION_DISPLAYED_ID); + } + Member editedMember = createEditedMember(memberToEdit, reservationToDelete); + model.setMember(memberToEdit, editedMember); + model.updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS); + return new CommandResult(String.format(MESSAGE_SUCCESS, "Id: " + editedMember.getId() + + "; Name: " + editedMember.getName() + + "; Reservation: " + "[" + reservationToDelete + "]")); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteReservationCommand // instanceof handles nulls + && memberId.equals(((DeleteReservationCommand) other).memberId) + && reservationId.equals(((DeleteReservationCommand) other).reservationId)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/DeleteTransactionCommand.java b/src/main/java/seedu/address/logic/commands/DeleteTransactionCommand.java new file mode 100644 index 00000000000..3c0671d3558 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeleteTransactionCommand.java @@ -0,0 +1,138 @@ +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_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TRANSACTION; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_MEMBERS; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.Timestamp; +import seedu.address.model.member.Address; +import seedu.address.model.member.Credit; +import seedu.address.model.member.Email; +import seedu.address.model.member.Id; +import seedu.address.model.member.Member; +import seedu.address.model.member.Name; +import seedu.address.model.member.Phone; +import seedu.address.model.member.Point; +import seedu.address.model.reservation.Reservation; +import seedu.address.model.tag.Tag; +import seedu.address.model.transaction.Transaction; + +/** + * Deletes a transaction identified by it's member ID and transaction ID from the ezFoodie. + */ +public class DeleteTransactionCommand extends DeleteCommand { + + /** + * Stands for delete command. + */ + public static final String COMMAND_WORD = "del"; + + /** + * Stands for the message of delete command related to transaction. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the transaction identified by the member ID and transaction ID.\n" + + "Parameters:\n" + + "Delete by member ID and transaction ID: " + + PREFIX_TRANSACTION + " " + PREFIX_ID + "member ID + transaction ID\n" + + "Example:\n" + + "Delete by member ID and transaction ID: " + + COMMAND_WORD + " " + PREFIX_TRANSACTION + " " + PREFIX_ID + "00001000001"; + + /** + * Stands for succeed message of delete transaction. + */ + public static final String MESSAGE_SUCCESS = "Deleted Transaction: %1$s"; + + private final seedu.address.model.member.Id memberId; + private final seedu.address.model.transaction.Id transactionId; + + /** + * Constructs an DeleteCommand to delete the specified {@code Member} by {@code memberId} and {@code transactionId}. + */ + public DeleteTransactionCommand( + seedu.address.model.member.Id memberId, seedu.address.model.transaction.Id transactionId) { + requireAllNonNull(memberId, transactionId); + this.memberId = memberId; + this.transactionId = transactionId; + } + + /** + * Creates and returns a {@code Member} with the details of {@code memberToEdit} and {@code transactionToDelete} + * + * @param memberToEdit create a new member to edit and update. + * @param transactionToDelete the transaction will be deleted. + * @return Member with added transactions and updated credits. + */ + private static Member createEditedMember(Member memberToEdit, Transaction transactionToDelete) { + assert memberToEdit != null; + assert transactionToDelete != null; + + Id id = memberToEdit.getId(); + Name name = memberToEdit.getName(); + Phone phone = memberToEdit.getPhone(); + Email email = memberToEdit.getEmail(); + Address address = memberToEdit.getAddress(); + Timestamp timestamp = memberToEdit.getTimestamp(); + List transactions = memberToEdit.getTransactions(); + List reservations = memberToEdit.getReservations(); + Set updatedTags = memberToEdit.getTags(); + + List updatedTransactions = new ArrayList<>(transactions); + updatedTransactions.remove(transactionToDelete); + Credit updatedCredit = new Credit("" + Math.min(updatedTransactions.stream() + .mapToInt(t -> (int) t.getBilling().getDoubleValue()).sum(), Credit.MAX)); + Point point = memberToEdit.getPoint(); + return new Member(id, name, phone, email, address, timestamp, updatedCredit, + point, updatedTransactions, reservations, updatedTags); + } + + /** + * Executes the model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult delete transaction command. + * @throws CommandException if the user input does not conform the expected format. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getUpdatedMemberList(); + Member memberToEdit = lastShownList.stream() + .filter(member -> memberId.equals(member.getId())).findAny().orElse(null); + if (memberToEdit == null) { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_ID); + } + Transaction transactionToDelete = memberToEdit.getTransactions().stream() + .filter(transaction -> transactionId.equals(transaction.getId())).findAny().orElse(null); + if (transactionToDelete == null) { + throw new CommandException(Messages.MESSAGE_INVALID_TRANSACTION_DISPLAYED_ID); + } + Member editedMember = createEditedMember(memberToEdit, transactionToDelete); + model.setMember(memberToEdit, editedMember); + model.updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS); + return new CommandResult(String.format(MESSAGE_SUCCESS, "Id: " + editedMember.getId() + + "; Name: " + editedMember.getName() + + "; Transaction: " + "[" + transactionToDelete + "]")); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteTransactionCommand // instanceof handles nulls + && memberId.equals(((DeleteTransactionCommand) other).memberId) + && transactionId.equals(((DeleteTransactionCommand) other).transactionId)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java index 7e36114902f..56697df99ae 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/address/logic/commands/EditCommand.java @@ -1,226 +1,39 @@ 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_TAG; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESERVATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TRANSACTION; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.CollectionUtil; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.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; /** - * Edits the details of an existing person in the address book. + * Represents a edit command to add member or transaction to ezFoodie. */ -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) " - + "[" + PREFIX_NAME + "NAME] " - + "[" + 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; - private final EditPersonDescriptor editPersonDescriptor; +public abstract class EditCommand extends Command { /** - * @param index of the person in the filtered person list to edit - * @param editPersonDescriptor details to edit the person with + * Stands for edit command. */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { - requireNonNull(index); - requireNonNull(editPersonDescriptor); - - this.index = index; - this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); - } - - @Override - public CommandResult execute(Model model) throws CommandException { - 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 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, editedPerson)); - } + public static final String COMMAND_WORD = "edit"; /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. + * Stands for the message of edit command. */ - private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { - assert personToEdit != null; - - Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); - Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); - Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); - Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); - Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditCommand)) { - return false; - } - - // state check - EditCommand e = (EditCommand) other; - return index.equals(e.index) - && editPersonDescriptor.equals(e.editPersonDescriptor); - } + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits a member or a transaction or a reservation in the ezFoodie.\n" + + "With " + + PREFIX_MEMBER + " (member details) or " + + PREFIX_TRANSACTION + " (transaction details) or " + + PREFIX_RESERVATION + " (reservation details)"; /** - * Stores the details to edit the person with. Each non-empty field value will replace the - * corresponding field value of the person. + * Overrides the executes command. + * + * @param model {@code Model} which the command should operate on. + * @throws CommandException if the user input does not conform the expected format. */ - public static class EditPersonDescriptor { - private Name name; - private Phone phone; - private Email email; - private Address address; - private Set tags; - - public EditPersonDescriptor() {} - - /** - * Copy constructor. - * A defensive copy of {@code tags} is used internally. - */ - public EditPersonDescriptor(EditPersonDescriptor toCopy) { - setName(toCopy.name); - setPhone(toCopy.phone); - setEmail(toCopy.email); - setAddress(toCopy.address); - setTags(toCopy.tags); - } - - /** - * Returns true if at least one field is edited. - */ - public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); - } - - 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); - } - - /** - * 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) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditPersonDescriptor)) { - return false; - } - - // state check - EditPersonDescriptor e = (EditPersonDescriptor) other; + @Override + public abstract CommandResult execute(Model model) throws CommandException; - return getName().equals(e.getName()) - && getPhone().equals(e.getPhone()) - && getEmail().equals(e.getEmail()) - && getAddress().equals(e.getAddress()) - && getTags().equals(e.getTags()); - } - } } diff --git a/src/main/java/seedu/address/logic/commands/EditMemberCommand.java b/src/main/java/seedu/address/logic/commands/EditMemberCommand.java new file mode 100644 index 00000000000..0d2a1204362 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/EditMemberCommand.java @@ -0,0 +1,327 @@ +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_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_MEMBERS; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.Timestamp; +import seedu.address.model.member.Address; +import seedu.address.model.member.Credit; +import seedu.address.model.member.Email; +import seedu.address.model.member.Id; +import seedu.address.model.member.Member; +import seedu.address.model.member.Name; +import seedu.address.model.member.Phone; +import seedu.address.model.member.Point; +import seedu.address.model.reservation.Reservation; +import seedu.address.model.tag.Tag; +import seedu.address.model.transaction.Transaction; + +/** + * Edits the details of an existing member in the ezFoodie. + */ +public class EditMemberCommand extends EditCommand { + + /** + * Stands for edit command. + */ + public static final String COMMAND_WORD = "edit"; + + /** + * Stands for the message of edit command. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the member identified " + + "by the index number used in the displayed member list or the member ID. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters:\n" + + "Edit by index number: " + PREFIX_MEMBER + " " + PREFIX_INDEX + "INDEX " + + "(INDEX must be a positive integer) " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_PHONE + "PHONE] " + + "[" + PREFIX_EMAIL + "EMAIL] " + + "[" + PREFIX_ADDRESS + "ADDRESS]\n" + + "Edit by member ID: " + PREFIX_MEMBER + " " + PREFIX_ID + "ID " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_PHONE + "PHONE] " + + "[" + PREFIX_EMAIL + "EMAIL] " + + "[" + PREFIX_ADDRESS + "ADDRESS]\n" + + "Example:\n" + + "Edit by index number: " + COMMAND_WORD + " " + PREFIX_MEMBER + " " + PREFIX_INDEX + "1 " + + PREFIX_PHONE + "91234567 " + + PREFIX_EMAIL + "johndoe@example.com\n" + + "Edit by member ID: " + COMMAND_WORD + " " + PREFIX_MEMBER + " " + PREFIX_ID + "00001 " + + PREFIX_PHONE + "91234567 " + + PREFIX_EMAIL + "johndoe@example.com"; + + /** + * Stands for succeed message of edit member. + */ + public static final String MESSAGE_SUCCESS = "Edited Member: %1$s"; + + /** + * Stands for message of not edited which need fields provided. + */ + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + + /** + * Stands for message of duplicate message. + */ + public static final String MESSAGE_DUPLICATE_MEMBER = + "This member (phone or email) already exists in the ezFoodie."; + + private final Index index; + private final Id id; + private final EditMemberDescriptor editMemberDescriptor; + + /** + * Constructs EditMemberCommand to edit member by index. + * + * @param index of the member in the updated member list to edit. + * @param editMemberDescriptor details to edit the member with. + */ + public EditMemberCommand(Index index, EditMemberDescriptor editMemberDescriptor) { + requireAllNonNull(index, editMemberDescriptor); + + this.index = index; + id = null; + this.editMemberDescriptor = new EditMemberDescriptor(editMemberDescriptor); + } + + /** + * Constructs EditMemberCommand to edit member by member id. + * + * @param id of the member in the updated member list to edit. + * @param editMemberDescriptor details to edit the member with. + */ + public EditMemberCommand(Id id, EditMemberDescriptor editMemberDescriptor) { + requireAllNonNull(id, editMemberDescriptor); + + this.id = id; + index = null; + this.editMemberDescriptor = new EditMemberDescriptor(editMemberDescriptor); + } + + /** + * Overrides and executes model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related edit member command. + * @throws CommandException if the user input does not conform the expected format. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getUpdatedMemberList(); + + Member memberToEdit = null; + if (index != null) { + if (index.getZeroBased() < lastShownList.size()) { + memberToEdit = lastShownList.get(index.getZeroBased()); + } else { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_INDEX); + } + } + if (id != null) { + memberToEdit = lastShownList.stream() + .filter(member -> id.equals(member.getId())).findAny().orElse(null); + } + if (memberToEdit != null) { + Member editedMember = createEditedMember(memberToEdit, editMemberDescriptor); + if (model.hasMember(editedMember, member -> member.getId() != editedMember.getId())) { + throw new CommandException(MESSAGE_DUPLICATE_MEMBER); + } + model.setMember(memberToEdit, editedMember); + model.updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS); + return new CommandResult(String.format(MESSAGE_SUCCESS, editedMember)); + } else { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_ID); + } + } + + /** + * Creates and returns a {@code Member} with the details of {@code memberToEdit} + * edited with {@code editMemberDescriptor}. + * @return Member with edited member. + */ + private static Member createEditedMember(Member memberToEdit, EditMemberDescriptor editMemberDescriptor) { + assert memberToEdit != null; + + Id id = memberToEdit.getId(); + Name updatedName = editMemberDescriptor.getName().orElse(memberToEdit.getName()); + Phone updatedPhone = editMemberDescriptor.getPhone().orElse(memberToEdit.getPhone()); + Email updatedEmail = editMemberDescriptor.getEmail().orElse(memberToEdit.getEmail()); + Address updatedAddress = editMemberDescriptor.getAddress().orElse(memberToEdit.getAddress()); + Timestamp timestamp = memberToEdit.getTimestamp(); + Credit credit = memberToEdit.getCredit(); + Point point = memberToEdit.getPoint(); + List transactions = memberToEdit.getTransactions(); + List reservations = memberToEdit.getReservations(); + Set updatedTags = editMemberDescriptor.getTags().orElse(memberToEdit.getTags()); + + return new Member(id, updatedName, updatedPhone, updatedEmail, updatedAddress, timestamp, credit, point, + transactions, reservations, updatedTags); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EditMemberCommand // instanceof handles nulls + && (index == null || index.equals(((EditMemberCommand) other).index)) + && (id == null || id.equals(((EditMemberCommand) other).id))) + && editMemberDescriptor.equals(((EditMemberCommand) other).editMemberDescriptor); // state check + } + + /** + * Stores the details to edit the member with. Each non-empty field value will replace the + * corresponding field value of the member. + */ + public static class EditMemberDescriptor { + private Name name; + private Phone phone; + private Email email; + private Address address; + private Set tags; + + public EditMemberDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code toCopy} is used internally. + */ + public EditMemberDescriptor(EditMemberDescriptor toCopy) { + setName(toCopy.name); + setPhone(toCopy.phone); + setEmail(toCopy.email); + setAddress(toCopy.address); + setTags(toCopy.tags); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); + } + + /** + * Sets name. + */ + public void setName(Name name) { + this.name = name; + } + + /** + * Gets name. + */ + public Optional getName() { + return Optional.ofNullable(name); + } + + /** + * Sets phone. + */ + public void setPhone(Phone phone) { + this.phone = phone; + } + + /** + * Gets phone. + */ + public Optional getPhone() { + return Optional.ofNullable(phone); + } + + /** + * Sets email. + */ + public void setEmail(Email email) { + this.email = email; + } + + /** + * Gets email. + */ + public Optional getEmail() { + return Optional.ofNullable(email); + } + + /** + * Sets address. + */ + public void setAddress(Address address) { + this.address = address; + } + + /** + * Gets address. + */ + public Optional
getAddress() { + return Optional.ofNullable(address); + } + + /** + * Sets {@code tags} to this object's {@code tags}. + * A defensive copy of {@code tags} is used internally. + * + * @param tags a list of tags. + */ + 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(); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditMemberDescriptor)) { + return false; + } + + // state check + EditMemberDescriptor e = (EditMemberDescriptor) other; + + return getName().equals(e.getName()) + && getPhone().equals(e.getPhone()) + && getEmail().equals(e.getEmail()) + && getAddress().equals(e.getAddress()) + && getTags().equals(e.getTags()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditReservationCommand.java b/src/main/java/seedu/address/logic/commands/EditReservationCommand.java new file mode 100644 index 00000000000..a8bcbf41d08 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/EditReservationCommand.java @@ -0,0 +1,277 @@ +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_DATE_TIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REMARK; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESERVATION; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_MEMBERS; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.Timestamp; +import seedu.address.model.member.Address; +import seedu.address.model.member.Credit; +import seedu.address.model.member.Email; +import seedu.address.model.member.Member; +import seedu.address.model.member.Name; +import seedu.address.model.member.Phone; +import seedu.address.model.member.Point; +import seedu.address.model.reservation.DateTime; +import seedu.address.model.reservation.Remark; +import seedu.address.model.reservation.Reservation; +import seedu.address.model.tag.Tag; +import seedu.address.model.transaction.Transaction; + +/** + * Edits the details of an existing reservation in the ezFoodie. + */ +public class EditReservationCommand extends EditCommand { + + /** + * Stands for edit command. + */ + public static final String COMMAND_WORD = "edit"; + + /** + * Stands for the message of edit reservation command. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the reservation identified " + + "by the member ID and reservation ID. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters:\n" + + "Edit by member ID and reservation ID: " + + PREFIX_RESERVATION + " " + PREFIX_ID + "member ID + reservation ID " + + "[" + PREFIX_DATE_TIME + "DATE_TIME] " + + "[" + PREFIX_REMARK + "REMARK]\n" + + "Example:\n" + + "Edit by member ID and reservation ID: " + + COMMAND_WORD + " " + PREFIX_RESERVATION + " " + PREFIX_ID + "00001000001 " + + PREFIX_DATE_TIME + "2021-12-01 13:00 " + + PREFIX_REMARK + "3 people"; + + /** + * Stands for succeed message of edit member. + */ + public static final String MESSAGE_SUCCESS = "Edited Member: %1$s"; + + /** + * Stands for message of the edited message has the same date. + */ + public static final String MESSAGE_SAME_DATE = "Only one reservation can be added within the same day. " + + "Previous reservation: %1$s"; + + /** + * Stands for message of not edited which need fields provided. + */ + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + private final seedu.address.model.member.Id memberId; + private final seedu.address.model.reservation.Id reservationId; + private final EditReservationDescriptor editReservationDescriptor; + + /** + * Constructs the EditReservationCommand. + * + * @param memberId of the member in the updated member list to edit. + * @param reservationId of the reservation in the reservation list to edit. + * @param editReservationDescriptor details to edit the reservation with. + */ + public EditReservationCommand( + seedu.address.model.member.Id memberId, seedu.address.model.reservation.Id reservationId, + EditReservationDescriptor editReservationDescriptor) { + requireAllNonNull(memberId, reservationId, editReservationDescriptor); + + this.memberId = memberId; + this.reservationId = reservationId; + this.editReservationDescriptor = new EditReservationDescriptor(editReservationDescriptor); + } + + /** + * Creates and returns a {@code Member} with the details of {@code memberToEdit}, + * {@code reservationToEdit} and {@code editReservationDescriptor}. + * + * @return Member with updated credits. + */ + private static Member createEditedMember( + Member memberToEdit, Reservation reservationToEdit, EditReservationDescriptor editReservationDescriptor) { + assert memberToEdit != null; + assert reservationToEdit != null; + + // Member + seedu.address.model.member.Id id = memberToEdit.getId(); + Name updatedName = memberToEdit.getName(); + Phone updatedPhone = memberToEdit.getPhone(); + Email updatedEmail = memberToEdit.getEmail(); + Address updatedAddress = memberToEdit.getAddress(); + Timestamp timestamp = memberToEdit.getTimestamp(); + Credit credit = memberToEdit.getCredit(); + Point point = memberToEdit.getPoint(); + List transactions = memberToEdit.getTransactions(); + List reservations = memberToEdit.getReservations(); + Set updatedTags = memberToEdit.getTags(); + + // Reservation + DateTime updatedDateTime = editReservationDescriptor.getDateTime().orElse(reservationToEdit.getDateTime()); + Remark updatedRemark = editReservationDescriptor.getRemark().orElse(reservationToEdit.getRemark()); + + List updatedReservations = new ArrayList<>(reservations); + Reservation updatedReservation = new Reservation(reservationToEdit.getId(), updatedDateTime, updatedRemark); + updatedReservations.stream() + .filter(reservation -> reservation.isSameId(reservationToEdit)).findAny() + .ifPresent(reservation -> updatedReservations + .set(updatedReservations.indexOf(reservation), updatedReservation)); + + return new Member(id, updatedName, updatedPhone, updatedEmail, updatedAddress, timestamp, credit, point, + transactions, updatedReservations, updatedTags); + } + + /** + * Overrides and executes the model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related to edit reservation command. + * @throws CommandException if the user input does not conform the expected format. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getUpdatedMemberList(); + Member memberToEdit = lastShownList.stream() + .filter(member -> memberId.equals(member.getId())).findAny().orElse(null); + if (memberToEdit == null) { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_ID); + } + Reservation reservationToEdit = memberToEdit.getReservations().stream() + .filter(reservation -> reservationId.equals(reservation.getId())).findAny().orElse(null); + if (reservationToEdit == null) { + throw new CommandException(Messages.MESSAGE_INVALID_RESERVATION_DISPLAYED_ID); + } + DateTime dateTimeToEdit = editReservationDescriptor.getDateTime().orElse(null); + Reservation reservationSameDate = null; + if (dateTimeToEdit != null) { + if (!Reservation.isValidDateTime(dateTimeToEdit)) { + throw new CommandException(Reservation.MESSAGE_CONSTRAINTS); + } + reservationSameDate = memberToEdit.getReservations().stream() + .filter(reservation -> !reservation.equals(reservationToEdit) + && reservation.isSameDate(dateTimeToEdit.getLocalDateTimeValue())) + .findAny().orElse(null); + } + if (reservationSameDate == null) { + Member editedMember = createEditedMember(memberToEdit, reservationToEdit, editReservationDescriptor); + model.setMember(memberToEdit, editedMember); + model.updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS); + Reservation updatedReservation = editedMember.getReservations().stream() + .filter(reservation -> reservationId.equals(reservation.getId())).findAny().orElse(null); + return new CommandResult(String.format(MESSAGE_SUCCESS, "Id: " + editedMember.getId() + + "; Name: " + editedMember.getName() + + "; Reservation: " + "[" + updatedReservation + "]")); + } + throw new CommandException(String.format(MESSAGE_SAME_DATE, reservationSameDate)); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EditReservationCommand // instanceof handles nulls + && memberId.equals(((EditReservationCommand) other).memberId) + && reservationId.equals(((EditReservationCommand) other).reservationId) + && editReservationDescriptor + .equals(((EditReservationCommand) other).editReservationDescriptor)); // state check + } + + /** + * Stores the details to edit the reservation with. Each non-empty field value will replace the + * corresponding field value of the reservation. + */ + public static class EditReservationDescriptor { + private DateTime dateTime; + private Remark remark; + + /** + * Constructs the EditReservationDescriptor without input. + */ + public EditReservationDescriptor() {} + + /** + * Copies constructor. + * A defensive copy of {@code toCopy} is used internally. + */ + public EditReservationDescriptor(EditReservationDescriptor toCopy) { + setDateTime(toCopy.dateTime); + setRemark(toCopy.remark); + } + + /** + * Returns true if at least one field is edited. + * + * @return boolean if true some filed is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(dateTime, remark); + } + + /** + * Sets DateTime. + * + * @param dateTime the date time for editing. + */ + public void setDateTime(DateTime dateTime) { + this.dateTime = dateTime; + } + + /** + * Gets DateTime. + */ + public Optional getDateTime() { + return Optional.ofNullable(dateTime); + } + + /** + * Sets remark from {@code remark}. + */ + public void setRemark(Remark remark) { + this.remark = remark; + } + + /** + * Gets remark. + */ + public Optional getRemark() { + return Optional.ofNullable(remark); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditReservationDescriptor)) { + return false; + } + + // state check + EditReservationDescriptor e = (EditReservationDescriptor) other; + + return getDateTime().equals(e.getDateTime()) + && getRemark().equals(e.getRemark()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditTransactionCommand.java b/src/main/java/seedu/address/logic/commands/EditTransactionCommand.java new file mode 100644 index 00000000000..e68c588c94c --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/EditTransactionCommand.java @@ -0,0 +1,259 @@ +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_BILLING; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TRANSACTION; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_MEMBERS; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.Timestamp; +import seedu.address.model.member.Address; +import seedu.address.model.member.Credit; +import seedu.address.model.member.Email; +import seedu.address.model.member.Member; +import seedu.address.model.member.Name; +import seedu.address.model.member.Phone; +import seedu.address.model.member.Point; +import seedu.address.model.reservation.Reservation; +import seedu.address.model.tag.Tag; +import seedu.address.model.transaction.Billing; +import seedu.address.model.transaction.Transaction; + +/** + * Edits the details of an existing transaction in the ezFoodie. + */ +public class EditTransactionCommand extends EditCommand { + + /** + * Stands for edit command. + */ + public static final String COMMAND_WORD = "edit"; + + /** + * Stands for the message of edit command for edit transaction. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the transaction identified " + + "by the member ID and transaction ID. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters:\n" + + "Edit by member ID and transaction ID: " + + PREFIX_TRANSACTION + " " + PREFIX_ID + "member ID + transaction ID " + + PREFIX_BILLING + "BILLING_AMOUNT\n" + + "Example:\n" + + "Edit by member ID and transaction ID: " + + COMMAND_WORD + " " + PREFIX_TRANSACTION + " " + PREFIX_ID + "00001000001 " + + PREFIX_BILLING + "123.45"; + + /** + * Stands for succeed message of edit member. + */ + public static final String MESSAGE_SUCCESS = "Edited Member: %1$s"; + + /** + * Stands for message of not edited which need fields provided. + */ + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + + private final seedu.address.model.member.Id memberId; + private final seedu.address.model.transaction.Id transactionId; + private final EditTransactionDescriptor editTransactionDescriptor; + + /** + * Constructs EditTransactionCommand. + * + * @param memberId of the member in the updated member list to edit. + * @param transactionId of the transaction in the transaction list to edit. + * @param editTransactionDescriptor details to edit the transaction with. + */ + public EditTransactionCommand( + seedu.address.model.member.Id memberId, seedu.address.model.transaction.Id transactionId, + EditTransactionDescriptor editTransactionDescriptor) { + requireAllNonNull(memberId, transactionId, editTransactionDescriptor); + + this.memberId = memberId; + this.transactionId = transactionId; + this.editTransactionDescriptor = new EditTransactionDescriptor(editTransactionDescriptor); + } + + /** + * Creates and returns a {@code Member} with the details of {@code memberToEdit}, + * {@code transactionToEdit} and {@code editTransactionDescriptor}. + * + * @return Member with updated credits. + */ + private static Member createEditedMember( + Member memberToEdit, Transaction transactionToEdit, EditTransactionDescriptor editTransactionDescriptor) { + assert memberToEdit != null; + assert transactionToEdit != null; + + // Member + seedu.address.model.member.Id id = memberToEdit.getId(); + Name name = memberToEdit.getName(); + Phone phone = memberToEdit.getPhone(); + Email email = memberToEdit.getEmail(); + Address address = memberToEdit.getAddress(); + Timestamp timestamp = memberToEdit.getTimestamp(); + List transactions = memberToEdit.getTransactions(); + List reservations = memberToEdit.getReservations(); + Set updatedTags = memberToEdit.getTags(); + + // Transaction + Timestamp updatedTimestamp = editTransactionDescriptor.getTimestamp().orElse(transactionToEdit.getTimestamp()); + Billing updatedBilling = editTransactionDescriptor.getBilling().orElse(transactionToEdit.getBilling()); + + List updatedTransactions = new ArrayList<>(transactions); + Transaction updatedTransaction = new Transaction(transactionToEdit.getId(), updatedTimestamp, updatedBilling); + updatedTransactions.stream() + .filter(transaction -> transaction.isSameId(transactionToEdit)).findAny() + .ifPresent(transaction -> updatedTransactions + .set(updatedTransactions.indexOf(transaction), updatedTransaction)); + Credit updatedCredit = new Credit("" + Math.min(updatedTransactions.stream() + .mapToInt(t -> (int) t.getBilling().getDoubleValue()).sum(), Credit.MAX)); + + Point updatedPoint; + if (updatedBilling.getDoubleValue() > transactionToEdit.getBilling().getDoubleValue()) { + updatedPoint = new Point("" + Math.min(Integer.parseInt(String.valueOf(updatedCredit.getIntValue() + - memberToEdit.getCredit().getIntValue() + + memberToEdit.getPoint().getIntValue())), Point.MAX)); + } else { + updatedPoint = memberToEdit.getPoint(); + } + return new Member(id, name, phone, email, address, timestamp, updatedCredit, + updatedPoint, updatedTransactions, reservations, updatedTags); + } + + /** + * Overrides and executes model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related to edir transaction command. + * @throws CommandException if the user input does not conform the expected format. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getUpdatedMemberList(); + Member memberToEdit = lastShownList.stream() + .filter(member -> memberId.equals(member.getId())).findAny().orElse(null); + if (memberToEdit == null) { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_ID); + } + Transaction transactionToEdit = memberToEdit.getTransactions().stream() + .filter(transaction -> transactionId.equals(transaction.getId())).findAny().orElse(null); + if (transactionToEdit == null) { + throw new CommandException(Messages.MESSAGE_INVALID_TRANSACTION_DISPLAYED_ID); + } + Member editedMember = createEditedMember(memberToEdit, transactionToEdit, editTransactionDescriptor); + model.setMember(memberToEdit, editedMember); + model.updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS); + Transaction updatedTransaction = editedMember.getTransactions().stream() + .filter(transaction -> transactionId.equals(transaction.getId())).findAny().orElse(null); + return new CommandResult(String.format(MESSAGE_SUCCESS, "Id: " + editedMember.getId() + + "; Name: " + editedMember.getName() + + "; Transaction: " + "[" + updatedTransaction + "]")); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EditTransactionCommand // instanceof handles nulls + && memberId.equals(((EditTransactionCommand) other).memberId) + && transactionId.equals(((EditTransactionCommand) other).transactionId) + && editTransactionDescriptor + .equals(((EditTransactionCommand) other).editTransactionDescriptor)); // state check + } + + /** + * Stores the details to edit the transaction with. Each non-empty field value will replace the + * corresponding field value of the transaction. + */ + public static class EditTransactionDescriptor { + private Timestamp timestamp; + private Billing billing; + + public EditTransactionDescriptor() {} + + /** + * Copies constructor. + * A defensive copy of {@code toCopy} is used internally. + */ + public EditTransactionDescriptor(EditTransactionDescriptor toCopy) { + setTimestamp(toCopy.timestamp); + setBilling(toCopy.billing); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(timestamp, billing); + } + + /** + * Sets time stamp from input {@code timestamp}. + * + * @param timestamp transaction's timestamp. + */ + public void setTimestamp(Timestamp timestamp) { + this.timestamp = timestamp; + } + + /** + * Gets time stamp. + */ + public Optional getTimestamp() { + return Optional.ofNullable(timestamp); + } + + /** + * Sets Billing from {@code billing}. + * + * @param billing transaction's billing. + */ + public void setBilling(Billing billing) { + this.billing = billing; + } + + /** + * Gets billing. + */ + public Optional getBilling() { + return Optional.ofNullable(billing); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditTransactionDescriptor)) { + return false; + } + + // state check + EditTransactionDescriptor e = (EditTransactionDescriptor) other; + + return getTimestamp().equals(e.getTimestamp()) + && getBilling().equals(e.getBilling()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java index 3dd85a8ba90..2552468f0f2 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java @@ -7,13 +7,25 @@ */ public class ExitCommand extends Command { + /** + * Stands for exit command. + */ public static final String COMMAND_WORD = "exit"; - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; + /** + * Stands for the message of exit success command. + */ + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting ezFoodie as requested ..."; + /** + * Overrides and executes the model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related to edit command. + */ @Override public CommandResult execute(Model model) { - return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false, false); } } diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java index d6b19b0a0de..245b6dc526d 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/seedu/address/logic/commands/FindCommand.java @@ -1,38 +1,119 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; + +import java.util.function.Predicate; import seedu.address.commons.core.Messages; import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.member.EmailContainsKeywordsPredicate; +import seedu.address.model.member.IdContainsKeywordsPredicate; +import seedu.address.model.member.Member; +import seedu.address.model.member.NameContainsKeywordsPredicate; +import seedu.address.model.member.PhoneContainsKeywordsPredicate; +import seedu.address.model.member.RegistrationDateContainsKeywordsPredicate; /** - * Finds and lists all persons in address book whose name contains any of the argument keywords. + * Finds and lists all members in ezFoodie whose name contains any of the argument keywords. * Keyword matching is case insensitive. */ public class FindCommand extends Command { + /** + * Stands for find 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"; + /** + * Stands for the message of find command. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Finds all members whose ids, names, phones or emails contain any of " + + "the specified keywords (case-insensitive) or within a specific of registration dates " + + "and displays them as a list with index numbers.\n" + + "Parameters: (can be multiple keywords)\n" + + "Find by member ID: " + PREFIX_MEMBER + " [" + PREFIX_ID + "ID]...\n" + + "Find by name: " + PREFIX_MEMBER + " [" + PREFIX_NAME + "NAME]...\n" + + "Find by phone: " + PREFIX_MEMBER + " [" + PREFIX_PHONE + "PHONE]...\n" + + "Find by email: " + PREFIX_MEMBER + " [" + PREFIX_EMAIL + "EMAIL]...\n" + + "Find by registration date: " + PREFIX_MEMBER + " [" + PREFIX_DATE + "REGISTRATION_DATE]...\n" + + "Example:\n" + + "Find by member ID: " + COMMAND_WORD + " " + PREFIX_MEMBER + " " + PREFIX_ID + "00001 00002\n" + + "Find by name: " + COMMAND_WORD + " " + PREFIX_MEMBER + " " + PREFIX_NAME + "Alex Yu\n" + + "Find by phone: " + COMMAND_WORD + " " + PREFIX_MEMBER + " " + PREFIX_PHONE + "87438807 93210283\n" + + "Find by email: " + COMMAND_WORD + " " + PREFIX_MEMBER + " " + PREFIX_EMAIL + + "alexyeoh@example.com irfan@example.com\n" + + "Find by registration date: " + COMMAND_WORD + " " + PREFIX_MEMBER + + " " + PREFIX_DATE + " 2021-01-02 2021-01-03"; + + private final Predicate predicate; - private final NameContainsKeywordsPredicate predicate; + /** + * Constructs FindCommand through Id from input {@code predicate}. + */ + public FindCommand(IdContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + /** + * Constructs FindCommand through Name {@code predicate}. + * + * @param predicate the details of contain key words for name. + */ public FindCommand(NameContainsKeywordsPredicate predicate) { this.predicate = predicate; } + /** + * Constructs FindCommand through Phone. + * + * @param predicate the details of contain key words for phone. + */ + public FindCommand(PhoneContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + /** + * Constructs FindCommand through Email {@code predicate}. + * + * @param predicate the details of contain key words for email. + */ + public FindCommand(EmailContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + /** + * Constructs FindCommand through RegistrationDate {@code predicate}. + * + * @param predicate the details of contain key words for registration date. + */ + public FindCommand(RegistrationDateContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + /** + * Executes the model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related to find command. + */ @Override public CommandResult execute(Model model) { requireNonNull(model); - model.updateFilteredPersonList(predicate); + model.updateFilteredMemberList(predicate); return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); + String.format(Messages.MESSAGE_MEMBERS_LISTED_OVERVIEW, model.getUpdatedMemberList().size())); } + /** + * Overrides the equals method. + */ @Override public boolean equals(Object other) { return other == this // short circuit if same object diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java index bf824f91bd0..88ba48ad120 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java @@ -3,19 +3,34 @@ import seedu.address.model.Model; /** - * Format full help instructions for every command for display. + * Formats full help instructions for every command for display. */ public class HelpCommand extends Command { + /** + * Stands for help command. + */ public static final String COMMAND_WORD = "help"; + /** + * Stands for the message of help command. + */ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions.\n" + "Example: " + COMMAND_WORD; + /** + * Stands for the message of help command and show help window. + */ public static final String SHOWING_HELP_MESSAGE = "Opened help window."; + /** + * Overrides and executes the model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related to help command. + */ @Override public CommandResult execute(Model model) { - return new CommandResult(SHOWING_HELP_MESSAGE, true, false); + return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, false); } } diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java index 84be6ad2596..4575bc3e76e 100644 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ b/src/main/java/seedu/address/logic/commands/ListCommand.java @@ -1,24 +1,52 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_MEMBERS; import seedu.address.model.Model; /** - * Lists all persons in the address book to the user. + * Lists all members in the ezFoodie to the user. */ public class ListCommand extends Command { + /** + * Stands for list command. + */ public static final String COMMAND_WORD = "list"; - public static final String MESSAGE_SUCCESS = "Listed all persons"; + /** + * Stands for the message of list command. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Lists out all members.\n" + + "Parameters: " + PREFIX_MEMBER + "\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_MEMBER; + /** + * Stands for the message success listed. + */ + public static final String MESSAGE_SUCCESS = "Listed all members."; + /** + * Overrides and Executes the model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related to list command. + */ @Override public CommandResult execute(Model model) { requireNonNull(model); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + model.updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS); return new CommandResult(MESSAGE_SUCCESS); } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other instanceof ListCommand; + } } diff --git a/src/main/java/seedu/address/logic/commands/LoginCommand.java b/src/main/java/seedu/address/logic/commands/LoginCommand.java new file mode 100644 index 00000000000..99696707ccc --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/LoginCommand.java @@ -0,0 +1,79 @@ +package seedu.address.logic.commands; + +import seedu.address.commons.status.LoginStatus; +import seedu.address.model.Model; +import seedu.address.model.account.Password; + +/** + * Logs in as a manager. + */ +public class LoginCommand extends Command { + + /** + * Stands for login command. + */ + public static final String COMMAND_WORD = "login"; + + /** + * Stands for the message of login command. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Logs in as a manager by password.\n" + + "Parameters: PASSWORD (case-sensitive and must be not empty, default by " + + Password.DEFAULT_PLAINTEXT_PASSWORD + ")\n" + + "Example: " + COMMAND_WORD + " " + Password.DEFAULT_PLAINTEXT_PASSWORD; + + /** + * Stands for the message of login successfully. + */ + public static final String MESSAGE_SUCCESS = "Logged in successfully!"; + + /** + * Stands for the message of login failed. + */ + public static final String MESSAGE_FAILURE = "Failed to login!"; + + /** + * Stands for the message of login status. + */ + public static final String MESSAGE_ALREADY_IN_STATUS = "You are already in MANAGER login status!"; + + private final Password password; + + /** + * Constructs LoginCommand. + * + * @param password the login password. + */ + public LoginCommand(Password password) { + this.password = password; + } + + /** + * Overrides and executes the model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related to login command. + */ + @Override + public CommandResult execute(Model model) { + if (LoginStatus.getLoginStatus() == LoginStatus.MANAGER) { + return new CommandResult(MESSAGE_ALREADY_IN_STATUS); + } + String inputPassword = password.value; + String readPassword = model.getAccount().getPassword().value; + if (inputPassword.equals(readPassword)) { + LoginStatus.setLoginStatus(LoginStatus.MANAGER); + return new CommandResult(MESSAGE_SUCCESS); + } + return new CommandResult(MESSAGE_FAILURE); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other instanceof LoginCommand; + } +} diff --git a/src/main/java/seedu/address/logic/commands/LogoutCommand.java b/src/main/java/seedu/address/logic/commands/LogoutCommand.java new file mode 100644 index 00000000000..e602e02c8b8 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/LogoutCommand.java @@ -0,0 +1,47 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.model.Model.COMPARATOR_SORT_MEMBERS_BY_ID_ASC; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_MEMBERS; + +import seedu.address.commons.status.LoginStatus; +import seedu.address.model.Model; + +/** + * Logs out as a manager. + */ +public class LogoutCommand extends Command { + + /** + * Stands for logout command. + */ + public static final String COMMAND_WORD = "logout"; + + /** + * Stands for the message of logout successfully. + */ + public static final String MESSAGE_SUCCESS = "Logged out successfully!"; + + /** + * Stands for the message of logout status. + */ + public static final String MESSAGE_ALREADY_IN_STATUS = "You are already in STAFF login status!"; + + /** + * Overrides and executes the model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related to logout command. + */ + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + if (LoginStatus.getLoginStatus() == LoginStatus.STAFF) { + return new CommandResult(MESSAGE_ALREADY_IN_STATUS); + } + LoginStatus.setLoginStatus(LoginStatus.STAFF); + model.updateSortedMemberList(COMPARATOR_SORT_MEMBERS_BY_ID_ASC); + model.updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/address/logic/commands/RedeemCommand.java b/src/main/java/seedu/address/logic/commands/RedeemCommand.java new file mode 100644 index 00000000000..e936ed5b785 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/RedeemCommand.java @@ -0,0 +1,177 @@ +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_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REDEEM; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_MEMBERS; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.Timestamp; +import seedu.address.model.member.Address; +import seedu.address.model.member.Credit; +import seedu.address.model.member.Email; +import seedu.address.model.member.Id; +import seedu.address.model.member.Member; +import seedu.address.model.member.Name; +import seedu.address.model.member.Phone; +import seedu.address.model.member.Point; +import seedu.address.model.reservation.Reservation; +import seedu.address.model.tag.Tag; +import seedu.address.model.transaction.Transaction; + +/** + * Redeems point from an existing member in the ezFoodie. + */ +public class RedeemCommand extends Command { + + /** + * Stands for redeem command. + */ + public static final String COMMAND_WORD = "redeem"; + + /** + * Stands for the message of redeem command. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Redeems points from member id in the ezFoodie.\n" + + "Parameters:\n" + + "Redeem by index number: " + PREFIX_INDEX + "INDEX " + + "(INDEX must be a positive integer) " + + "[" + PREFIX_REDEEM + " POINTS]...\n" + + "Redeem by member ID: " + PREFIX_ID + "ID " + + "[" + PREFIX_REDEEM + " POINTS]...\n" + + "Example:\n" + + "Redeem by index number: " + COMMAND_WORD + " " + PREFIX_INDEX + "1 " + + PREFIX_REDEEM + "10 " + + PREFIX_REDEEM + "20\n" + + "Redeem by member ID: " + COMMAND_WORD + " " + PREFIX_ID + "00001 " + + PREFIX_REDEEM + "10 " + + PREFIX_REDEEM + "20"; + + /** + * Stands for message for redeem points successfully. + */ + public static final String MESSAGE_SUCCESS_REDEMPTION = "Redeemed Member: %1$s"; + + /** + * Stands for message for duplicate member. + */ + public static final String MESSAGE_DUPLICATE_MEMBER = + "This member (phone or email) already exists in the ezFoodie."; + + /** + * Stands for message when redemption point exceed. + */ + public static final String MESSAGE_INVALID_POINTS_LESS_THAN_ZERO = "Redeemed point has already exceeded.\n" + + "Points can't redeemed less than 0, please try again."; + + private final List pointsToRedeemList = new ArrayList<>(); + private final Id idToRedeem; + private final Index indexToRedeem; + + /** + * Constructs an RedeemCommand to add the specified {@code Member} by id. + * + * @param pointsToRedeemList the points of to redeemed list. + * @param id the member id that needs to redeem point. + */ + public RedeemCommand(List pointsToRedeemList, Id id) { + requireAllNonNull(pointsToRedeemList, id); + this.pointsToRedeemList.addAll(pointsToRedeemList); + this.idToRedeem = id; + this.indexToRedeem = null; + } + + /** + * Constructs an RedeemCommand to add the specified {@code Member} by index. + * + * @param pointsToRedeemList the points of to redeemed list. + * @param index the member index that needs to redeem point. + */ + public RedeemCommand(List pointsToRedeemList, Index index) { + requireAllNonNull(pointsToRedeemList, index); + this.pointsToRedeemList.addAll(pointsToRedeemList); + this.indexToRedeem = index; + this.idToRedeem = null; + } + + /** + * Overrides and executes the model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related to redeem command. + * @throws CommandException if the user input does not conform the expected format. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getUpdatedMemberList(); + + Member memberToEdit = null; + if (indexToRedeem != null) { + if (indexToRedeem.getZeroBased() < lastShownList.size()) { + memberToEdit = lastShownList.get(indexToRedeem.getZeroBased()); + } else { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_INDEX); + } + } + if (idToRedeem != null) { + memberToEdit = lastShownList.stream() + .filter(member -> idToRedeem.equals(member.getId())).findAny().orElse(null); + } + if (memberToEdit != null) { + Member toRedeemPointsMember = createToRedeemPointsMember(memberToEdit, pointsToRedeemList); + if (model.hasMember(toRedeemPointsMember, member -> member.getId() != toRedeemPointsMember.getId())) { + throw new CommandException(MESSAGE_DUPLICATE_MEMBER); + } + model.setMember(memberToEdit, toRedeemPointsMember); + model.updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS); + return new CommandResult(String.format(MESSAGE_SUCCESS_REDEMPTION, toRedeemPointsMember)); + } else { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_ID); + } + } + + /** + * Creates and returns a {@code Member} with the details of {@code memberToEdit} + * edited with {@code editMemberDescriptor}. + * + * @param memberToRedeemPoints creates the member who need to be redeemed points. + * @param toRedeemPointsList the list of points need to redeem all. + * @return Member with redeemed Points + * @throws CommandException if the user input does not conform the expected format. + */ + private static Member createToRedeemPointsMember(Member memberToRedeemPoints, List toRedeemPointsList) + throws CommandException { + assert memberToRedeemPoints != null; + + Id id = memberToRedeemPoints.getId(); + Name name = memberToRedeemPoints.getName(); + Phone phone = memberToRedeemPoints.getPhone(); + Email email = memberToRedeemPoints.getEmail(); + Address address = memberToRedeemPoints.getAddress(); + Timestamp timestamp = memberToRedeemPoints.getTimestamp(); + Set tags = memberToRedeemPoints.getTags(); + List transactions = memberToRedeemPoints.getTransactions(); + List reservations = memberToRedeemPoints.getReservations(); + Credit credit = memberToRedeemPoints.getCredit(); + Point toRedeemPointsSum = new Point("" + Math.min(toRedeemPointsList.stream() + .mapToInt(pointToUpdate -> (int) pointToUpdate.getDoubleValue()).sum(), Point.MAX)); + int oldPoint = memberToRedeemPoints.getPoint().getIntValue(); + int toRedeemPoint = toRedeemPointsSum.getIntValue(); + if (oldPoint < toRedeemPoint) { + throw new CommandException(MESSAGE_INVALID_POINTS_LESS_THAN_ZERO); + } + Point updatePoint = new Point(String.valueOf(oldPoint - toRedeemPoint)); + return new Member(id, name, phone, email, address, timestamp, credit, + updatePoint, transactions, reservations, tags); + } +} diff --git a/src/main/java/seedu/address/logic/commands/SetAccountCommand.java b/src/main/java/seedu/address/logic/commands/SetAccountCommand.java new file mode 100644 index 00000000000..b0ec919da6e --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SetAccountCommand.java @@ -0,0 +1,130 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PASS; + +import java.util.Optional; + +import seedu.address.commons.util.CollectionUtil; +import seedu.address.model.Account; +import seedu.address.model.Model; +import seedu.address.model.account.Password; + +/** + * Edits the details of an existing account in the ezFoodie. + */ +public class SetAccountCommand extends Command { + + public static final String COMMAND_WORD = "set"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sets login password. " + + "Existing password will be overwritten by the new password.\n" + + "Parameters: " + + PREFIX_PASS + "PASSWORD\n" + + "Example: " + + COMMAND_WORD + " " + PREFIX_PASS + "123456"; + + public static final String MESSAGE_SUCCESS = "Password updated."; + + private final EditAccountDescriptor editAccountDescriptor; + + /** + * Constructs a {@code SetAccountCommand} with {@code editAccountDescriptor}. + * + * @param editAccountDescriptor details to edit the account with. + */ + public SetAccountCommand(EditAccountDescriptor editAccountDescriptor) { + requireNonNull(editAccountDescriptor); + this.editAccountDescriptor = editAccountDescriptor; + } + + /** + * Executes the model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related to set account command. + */ + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + + Password passwordToEdit = model.getAccount().getPassword(); + Account editedAccount = createEditedAccount(passwordToEdit, editAccountDescriptor); + model.setAccount(editedAccount); + return new CommandResult(MESSAGE_SUCCESS); + } + + /** + * Creates and returns a {@code Account} with the details of {@code passwordToEdit} + * edited with {@code editAccountDescriptor}. + */ + private static Account createEditedAccount(Password passwordToEdit, EditAccountDescriptor editAccountDescriptor) { + assert passwordToEdit != null; + + Password updatedPassword = editAccountDescriptor.getPassword().orElse(passwordToEdit); + return new Account(updatedPassword); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SetAccountCommand // instanceof handles nulls + && editAccountDescriptor.equals(((SetAccountCommand) other).editAccountDescriptor)); // state check + } + + /** + * Stores the details to edit the account with. Each non-empty field value will replace the + * corresponding field value of the account. + */ + public static class EditAccountDescriptor { + private Password password; + + /** + * Constructs a {@code EditAccountDescriptor}. + */ + public EditAccountDescriptor() {} + + /** + * Copies constructor. + * A defensive copy of {@code toCopy} is used internally. + */ + public EditAccountDescriptor(SetAccountCommand.EditAccountDescriptor toCopy) { + setPassword(toCopy.password); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(password); + } + + public void setPassword(Password password) { + this.password = password; + } + + public Optional getPassword() { + return Optional.ofNullable(password); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof SetAccountCommand.EditAccountDescriptor)) { + return false; + } + + // state check + SetAccountCommand.EditAccountDescriptor e = (SetAccountCommand.EditAccountDescriptor) other; + + return getPassword().equals(e.getPassword()); + } + } +} 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..5d27a8c6d16 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SortCommand.java @@ -0,0 +1,83 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ASC; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CREDIT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DESC; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; + +import seedu.address.commons.status.SortStatus; +import seedu.address.model.Model; +import seedu.address.model.member.CreditSortComparator; + +/** + * Sorts and lists all members in ezFoodie by credit. + */ +public class SortCommand extends Command { + + /** + * Stands for sort command. + */ + public static final String COMMAND_WORD = "sort"; + + /** + * Stands for the message of Sort command. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Sorts all members by credit in ascending or descending " + + "and displays them as a list with index numbers.\n" + + "Parameters:\n" + + "Sort credit in ascending: " + PREFIX_MEMBER + " " + PREFIX_CREDIT + " " + PREFIX_ASC + "\n" + + "Sort credit in descending: " + PREFIX_MEMBER + " " + PREFIX_CREDIT + " " + PREFIX_DESC + "\n" + + "Example:\n" + + "Sort credit in ascending: " + + COMMAND_WORD + " " + PREFIX_MEMBER + " " + PREFIX_CREDIT + " " + PREFIX_ASC + "\n" + + "Sort credit in descending: " + + COMMAND_WORD + " " + PREFIX_MEMBER + " " + PREFIX_CREDIT + " " + PREFIX_DESC; + + /** + * Stands for the message of sorted in ascending. + */ + public static final String MESSAGE_SORT_ASC = "Members sorted by credit in ascending!"; + + /** + * Stands for the message of sorted in descending. + */ + public static final String MESSAGE_SORT_DESC = "Members sorted by credit in descending!"; + + private final CreditSortComparator comparator; + + /** + * Constructs SortCommand by {@code comparator}. + */ + public SortCommand(CreditSortComparator comparator) { + this.comparator = comparator; + } + + /** + * Overrides and executes the model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related to sort command. + */ + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateSortedMemberList(comparator); + if (comparator.getSortStatus() == SortStatus.DESC) { + return new CommandResult(MESSAGE_SORT_DESC); + } else { + return new CommandResult(MESSAGE_SORT_ASC); + } + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SortCommand // instanceof handles nulls + && comparator.equals(((SortCommand) other).comparator)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/SummaryCommand.java b/src/main/java/seedu/address/logic/commands/SummaryCommand.java new file mode 100644 index 00000000000..c5a88b8e1aa --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SummaryCommand.java @@ -0,0 +1,51 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.model.Model; + +/** + * Formats the summary of members and transactions related information + * for display in text form. + */ +public class SummaryCommand extends Command { + + /** + * Stands for summary command. + */ + public static final String COMMAND_WORD = "summary"; + + /** + * Stands for the message of summary command. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Shows summary for members and transactions.\n" + + "Example: " + COMMAND_WORD; + + /** + * Stands for the message of open summary window successfully. + */ + public static final String SHOWING_SUMMARY_MESSAGE = "Opened summary window."; + + /** + * Overrides and executes model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related to summary command. + */ + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + return new CommandResult(SHOWING_SUMMARY_MESSAGE, + false, false, false, true); + } + + /** + * Override the equals method. + */ + @Override + public boolean equals(Object other) { + // short circuit if same object + return (other instanceof SummaryCommand); // instanceof handles nulls + } +} diff --git a/src/main/java/seedu/address/logic/commands/ViewCommand.java b/src/main/java/seedu/address/logic/commands/ViewCommand.java new file mode 100644 index 00000000000..bcb719bebfd --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ViewCommand.java @@ -0,0 +1,76 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.member.IdContainsKeywordsPredicate; + +/** + * Views specific member details in eZFoodie, accessed by member ID. + */ +public class ViewCommand extends Command { + + /** + * Stands for view command. + */ + public static final String COMMAND_WORD = "show"; + + /** + * Stands for the message of open view window successfully. + */ + public static final String SHOWING_VIEW_MESSAGE = "Opened view window."; + + /** + * Stands for the message of show and view command. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": View a specific member's details, " + + "accessed by member ID.\n" + + "Parameters:\n" + + PREFIX_MEMBER + " " + PREFIX_ID + "ID\n" + + "Example:\n" + + COMMAND_WORD + " " + PREFIX_MEMBER + " " + PREFIX_ID + "00001"; + + private final IdContainsKeywordsPredicate predicate; + + /** + * Constructs the view command based on member ID predicate. + * + * @param predicate of member ID. + */ + public ViewCommand(IdContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + /** + * Overrides and executes the model. + * + * @param model {@code Model} which the command should operate on. + * @return CommandResult related to View command. + * @throws CommandException if the user input does not conform the expected format. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + model.updateFilteredMemberListForView(predicate); + if (model.getUpdatedMemberListForView().size() > 0) { + return new CommandResult(SHOWING_VIEW_MESSAGE, false, false, true, false); + } else { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_ID); + } + } + + /** + * Override the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ViewCommand // instanceof handles nulls + && predicate.equals(((ViewCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java index a16bd14f2cd..ca1decfb4d8 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java @@ -1,9 +1,15 @@ package seedu.address.logic.commands.exceptions; +import seedu.address.logic.commands.Command; + /** * Represents an error which occurs during execution of a {@link Command}. */ public class CommandException extends Exception { + + /** + * Constructs a new {@code CommandException} with the specified detail {@code message}. + */ public CommandException(String message) { super(message); } diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java index 3b8bfa035e8..bdcb96a737f 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java @@ -1,60 +1,20 @@ package seedu.address.logic.parser; -import static seedu.address.commons.core.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_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import java.util.Set; -import java.util.stream.Stream; - import seedu.address.logic.commands.AddCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; /** - * Parses input arguments and creates a new AddCommand object + * Parses input arguments and creates a new AddCommandParser object. */ -public class AddCommandParser implements Parser { +public abstract class AddCommandParser { /** * 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 - */ - public AddCommand parse(String args) throws ParseException { - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); - - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) - || !argMultimap.getPreamble().isEmpty()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); - } - - Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); - Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); - Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); - Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); - Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); - - Person person = new Person(name, phone, email, address, tagList); - - return new AddCommand(person); - } - - /** - * Returns true if none of the prefixes contains empty {@code Optional} values in the given - * {@code ArgumentMultimap}. + * + * @param args the input arguments related add command. + * @return AddCommand the class for process input add command string. + * @throws ParseException if the user input does not conform the expected format. */ - private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { - return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); - } - + public abstract AddCommand parse(String args) throws ParseException; } diff --git a/src/main/java/seedu/address/logic/parser/AddCommandPrefixParser.java b/src/main/java/seedu/address/logic/parser/AddCommandPrefixParser.java new file mode 100644 index 00000000000..9fbaedae249 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddCommandPrefixParser.java @@ -0,0 +1,63 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESERVATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TRANSACTION; + +import seedu.address.commons.status.ExecutionStatus; +import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.Model; + +/** + * Distinguishes between add member and add transaction commands and + * returns an AddMemberCommandParser or AddTransactionCommandParser + * depends on the very first prefix appears after command word. + */ +public class AddCommandPrefixParser { + private final Model model; + private final ExecutionStatus executionStatus; + + /** + * Constructs a {@code AddCommandPrefixParser} with the given {@code Model} and {@code ExecutionStatus}. + * + * @param model of ezFoodie. + * @param executionStatus normal or test. + */ + public AddCommandPrefixParser(Model model, ExecutionStatus executionStatus) { + this.model = model; + this.executionStatus = executionStatus; + } + + /** + * Parses the given {@code String} of arguments in the context of the AddCommandParser + * and returns an AddCommandParser object for execution. + * + * @param args the input arguments related add command to be parsed. + * @return AddMemberCommandParser or AddTransactionCommandParser or AddReservationCommandParser. + * @throws ParseException if the user input does not conform the expected format. + */ + public AddCommandParser parse(String args) throws ParseException { + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize( + args, PREFIX_MEMBER, PREFIX_TRANSACTION, PREFIX_RESERVATION); + + if (argMultimap.getValue(PREFIX_MEMBER).isPresent() + && argMultimap.getValue(PREFIX_TRANSACTION).isEmpty() + && argMultimap.getValue(PREFIX_RESERVATION).isEmpty()) { + return new AddMemberCommandParser(model, executionStatus); + } else if (argMultimap.getValue(PREFIX_TRANSACTION).isPresent() + && argMultimap.getValue(PREFIX_MEMBER).isEmpty() + && argMultimap.getValue(PREFIX_RESERVATION).isEmpty()) { + return new AddTransactionCommandParser(model, executionStatus); + } else if (argMultimap.getValue(PREFIX_RESERVATION).isPresent() + && argMultimap.getValue(PREFIX_MEMBER).isEmpty() + && argMultimap.getValue(PREFIX_TRANSACTION).isEmpty()) { + return new AddReservationCommandParser(model, executionStatus); + } else { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddMemberCommandParser.java b/src/main/java/seedu/address/logic/parser/AddMemberCommandParser.java new file mode 100644 index 00000000000..66dc7c768ad --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddMemberCommandParser.java @@ -0,0 +1,117 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.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_MEMBER; +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.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; + +import seedu.address.commons.status.ExecutionStatus; +import seedu.address.commons.util.DateTimeUtil; +import seedu.address.logic.commands.AddMemberCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.Model; +import seedu.address.model.Timestamp; +import seedu.address.model.member.Address; +import seedu.address.model.member.Credit; +import seedu.address.model.member.Email; +import seedu.address.model.member.Id; +import seedu.address.model.member.Member; +import seedu.address.model.member.Name; +import seedu.address.model.member.Phone; +import seedu.address.model.member.Point; +import seedu.address.model.reservation.Reservation; +import seedu.address.model.tag.Tag; +import seedu.address.model.transaction.Transaction; + +/** + * Parses input arguments and creates a new AddMemberCommand object. + */ +public class AddMemberCommandParser extends AddCommandParser implements Parser { + + private static final String ID_STUB = "00001"; + + private final Model model; + private final ExecutionStatus executionStatus; + + /** + * Constructs a {@code AddMemberCommandParser} with the given {@code Model} and {@code ExecutionStatus}. + */ + public AddMemberCommandParser(Model model, ExecutionStatus executionStatus) { + this.model = model; + this.executionStatus = executionStatus; + } + + private String generateId() throws ParseException { + List memberList = model.getEzFoodie().getMemberList(); + long latestId = 0; + if (memberList.size() > 0) { + latestId = memberList.get(memberList.size() - 1).getId().getLongValue(); + } + if (latestId == Id.MAX) { + throw new ParseException(AddMemberCommand.MESSAGE_FULL); + } + return Long.toString(latestId + 1); + } + + private String generateIdStub() { + return ID_STUB; + } + + /** + * Parses the given {@code String} of arguments in the context of the AddMemberCommand + * and returns an AddMemberCommand object for execution. + * + * @param args the input arguments related add member command to be parsed. + * @return AddMemberCommand the class for process input add member command string. + * @throws ParseException if the user input does not conform the expected format. + */ + @Override + public AddMemberCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_MEMBER, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, + PREFIX_ADDRESS, PREFIX_TAG); + + if (!arePrefixesPresent(argMultimap, + PREFIX_MEMBER, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddMemberCommand.MESSAGE_USAGE)); + } + + Id id = executionStatus == ExecutionStatus.NORMAL + ? ParserUtil.parseMemberId(generateId()) + : ParserUtil.parseMemberId(generateIdStub()); + + 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()); + Timestamp timestamp = executionStatus == ExecutionStatus.NORMAL + ? ParserUtil.parseTimestamp(DateTimeUtil.generateTimestamp()) + : ParserUtil.parseTimestamp(DateTimeUtil.generateTimestampStub()); + Credit credit = new Credit("0"); + Point point = new Point(credit.getStringValue()); + Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); + List transactionList = new ArrayList<>(); + List reservationList = new ArrayList<>(); + Member member = new Member(id, name, phone, email, address, timestamp, credit, point, + transactionList, reservationList, tagList); + return new AddMemberCommand(member); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/AddReservationCommandParser.java b/src/main/java/seedu/address/logic/parser/AddReservationCommandParser.java new file mode 100644 index 00000000000..db4ecfa4f20 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddReservationCommandParser.java @@ -0,0 +1,101 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE_TIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REMARK; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESERVATION; + +import java.util.List; +import java.util.stream.Stream; + +import seedu.address.commons.status.ExecutionStatus; +import seedu.address.logic.commands.AddReservationCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.Model; +import seedu.address.model.member.Member; +import seedu.address.model.reservation.DateTime; +import seedu.address.model.reservation.Remark; +import seedu.address.model.reservation.Reservation; + +/** + * Parses input arguments and creates a new AddReservationCommand object. + */ +public class AddReservationCommandParser extends AddCommandParser implements Parser { + + private static final String ID_STUB = "000001"; + private final Model model; + private final ExecutionStatus executionStatus; + + /** + * Constructs a {@code AddReservationCommandParser} with the given {@code Model} and {@code ExecutionStatus}. + */ + public AddReservationCommandParser(Model model, ExecutionStatus executionStatus) { + this.model = model; + this.executionStatus = executionStatus; + } + + private String generateId(seedu.address.model.member.Id id) throws ParseException { + List lastShownList = model.getUpdatedMemberList(); + Member memberToEdit = lastShownList.stream() + .filter(member -> id.equals(member.getId())).findAny().orElse(null); + if (memberToEdit != null) { + List reservationList = memberToEdit.getReservations(); + long latestId = 0; + if (reservationList.size() > 0) { + latestId = reservationList.get(reservationList.size() - 1).getId().getLongValue(); + } + if (latestId == seedu.address.model.reservation.Id.MAX) { + throw new ParseException(AddReservationCommand.MESSAGE_FULL); + } + return Long.toString(latestId + 1); + } else { + throw new ParseException(MESSAGE_INVALID_MEMBER_DISPLAYED_ID); + } + } + + private String generateIdStub() { + return ID_STUB; + } + + /** + * Parses the given {@code String} of arguments in the context of the AddReservationCommand + * and returns an AddReservationCommand object for execution. + * + * @param args the input arguments related add reservation command to be parsed. + * @return AddReservationCommand the class for process input add reservation command string. + * @throws ParseException if the user input does not conform the expected format. + */ + @Override + public AddReservationCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_RESERVATION, PREFIX_DATE_TIME, PREFIX_REMARK, PREFIX_ID); + + if (!arePrefixesPresent(argMultimap, PREFIX_RESERVATION, PREFIX_DATE_TIME, PREFIX_REMARK, PREFIX_ID) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddReservationCommand.MESSAGE_USAGE)); + } + + DateTime dateTime = ParserUtil.parseDateTime(argMultimap.getValue(PREFIX_DATE_TIME).get()); + Remark remark = ParserUtil.parseRemark(argMultimap.getValue(PREFIX_REMARK).get()); + seedu.address.model.member.Id memberId = ParserUtil.parseMemberId(argMultimap.getValue(PREFIX_ID).get()); + seedu.address.model.reservation.Id reservationId = executionStatus == ExecutionStatus.NORMAL + ? ParserUtil.parseReservationId(generateId(memberId)) + : ParserUtil.parseReservationId(generateIdStub()); + + Reservation reservation = new Reservation(reservationId, dateTime, remark); + + return new AddReservationCommand(reservation, memberId); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/AddTransactionCommandParser.java b/src/main/java/seedu/address/logic/parser/AddTransactionCommandParser.java new file mode 100644 index 00000000000..9b611923502 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddTransactionCommandParser.java @@ -0,0 +1,104 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_BILLING; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TRANSACTION; + +import java.util.List; +import java.util.stream.Stream; + +import seedu.address.commons.status.ExecutionStatus; +import seedu.address.commons.util.DateTimeUtil; +import seedu.address.logic.commands.AddTransactionCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.Model; +import seedu.address.model.Timestamp; +import seedu.address.model.member.Member; +import seedu.address.model.transaction.Billing; +import seedu.address.model.transaction.Transaction; + +/** + * Parses input arguments and creates a new AddTransactionCommand object. + */ +public class AddTransactionCommandParser extends AddCommandParser implements Parser { + + private static final String ID_STUB = "000001"; + + private final Model model; + private final ExecutionStatus executionStatus; + + /** + * Constructs a {@code AddTransactionCommandParser} with the given {@code Model} and {@code ExecutionStatus}. + */ + public AddTransactionCommandParser(Model model, ExecutionStatus executionStatus) { + this.model = model; + this.executionStatus = executionStatus; + } + + private String generateId(seedu.address.model.member.Id id) throws ParseException { + List lastShownList = model.getUpdatedMemberList(); + Member memberToEdit = lastShownList.stream() + .filter(member -> id.equals(member.getId())).findAny().orElse(null); + if (memberToEdit != null) { + List transactionList = memberToEdit.getTransactions(); + long latestId = 0; + if (transactionList.size() > 0) { + latestId = transactionList.get(transactionList.size() - 1).getId().getLongValue(); + } + if (latestId == seedu.address.model.transaction.Id.MAX) { + throw new ParseException(AddTransactionCommand.MESSAGE_FULL); + } + return Long.toString(latestId + 1); + } else { + throw new ParseException(MESSAGE_INVALID_MEMBER_DISPLAYED_ID); + } + } + + private String generateIdStub() { + return ID_STUB; + } + + /** + * Parses the given {@code String} of arguments in the context of the AddTransactionCommand + * and returns an AddTransactionCommand object for execution. + * + * @param args the input arguments related add transaction command to be parsed. + * @return AddTransactionCommand the class for process input add transaction command string. + * @throws ParseException if the user input does not conform the expected format. + */ + @Override + public AddTransactionCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_TRANSACTION, PREFIX_BILLING, PREFIX_ID); + + if (!arePrefixesPresent(argMultimap, PREFIX_TRANSACTION, PREFIX_BILLING, PREFIX_ID) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddTransactionCommand.MESSAGE_USAGE)); + } + + Billing billing = ParserUtil.parseBilling(argMultimap.getValue(PREFIX_BILLING).get()); + Timestamp timestamp = executionStatus == ExecutionStatus.NORMAL + ? ParserUtil.parseTimestamp(DateTimeUtil.generateTimestamp()) + : ParserUtil.parseTimestamp(DateTimeUtil.generateTimestampStub()); + seedu.address.model.member.Id memberId = ParserUtil.parseMemberId(argMultimap.getValue(PREFIX_ID).get()); + seedu.address.model.transaction.Id transactionId = executionStatus == ExecutionStatus.NORMAL + ? ParserUtil.parseTransactionId(generateId(memberId)) + : ParserUtil.parseTransactionId(generateIdStub()); + + Transaction transaction = new Transaction(transactionId, timestamp, billing); + + return new AddTransactionCommand(transaction, memberId); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java deleted file mode 100644 index 1e466792b46..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import seedu.address.logic.commands.AddCommand; -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.ExitCommand; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.parser.exceptions.ParseException; - -/** - * Parses user input. - */ -public class AddressBookParser { - - /** - * Used for initial separation of command word and args. - */ - private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); - - /** - * 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 - */ - public Command parseCommand(String userInput) throws ParseException { - final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); - if (!matcher.matches()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); - } - - final String commandWord = matcher.group("commandWord"); - final String arguments = matcher.group("arguments"); - switch (commandWord) { - - case AddCommand.COMMAND_WORD: - return new AddCommandParser().parse(arguments); - - case EditCommand.COMMAND_WORD: - return new EditCommandParser().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(); - - case ExitCommand.COMMAND_WORD: - return new ExitCommand(); - - case HelpCommand.COMMAND_WORD: - return new HelpCommand(); - - default: - 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 954c8e18f8e..66a899d4f53 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java @@ -22,8 +22,8 @@ public class ArgumentMultimap { * Associates the specified argument value with {@code prefix} key in this map. * If the map previously contained a mapping for the key, the new value is appended to the list of existing values. * - * @param prefix Prefix key with which the specified argument value is to be associated - * @param argValue Argument value to be associated with the specified prefix key + * @param prefix Prefix key with which the specified argument value is to be associated. + * @param argValue Argument value to be associated with the specified prefix key. */ public void put(Prefix prefix, String argValue) { List argValues = getAllValues(prefix); @@ -57,4 +57,11 @@ public List getAllValues(Prefix prefix) { public String getPreamble() { return getValue(new Prefix("")).orElse(""); } + + /** + * Returns the size of prefixes. + */ + public int getSize() { + return argMultimap.size(); + } } diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java index 5c9aebfa488..6dbf0c07a23 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java @@ -19,9 +19,9 @@ public class ArgumentTokenizer { * Tokenizes an arguments string and returns an {@code ArgumentMultimap} object that maps prefixes to their * respective argument values. Only the given prefixes will be recognized in the arguments string. * - * @param argsString Arguments string of the form: {@code preamble value value ...} - * @param prefixes Prefixes to tokenize the arguments string with - * @return ArgumentMultimap object that maps prefixes to their arguments + * @param argsString Arguments string of the form: {@code preamble value value ...}. + * @param prefixes Prefixes to tokenize the arguments string with. + * @return ArgumentMultimap object that maps prefixes to their arguments. */ public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) { List positions = findAllPrefixPositions(argsString, prefixes); @@ -31,9 +31,9 @@ public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) { /** * Finds all zero-based prefix positions in the given arguments string. * - * @param argsString Arguments string of the form: {@code preamble value value ...} - * @param prefixes Prefixes to find in the arguments string - * @return List of zero-based prefix positions in the given arguments string + * @param argsString Arguments string of the form: {@code preamble value value ...}. + * @param prefixes Prefixes to find in the arguments string. + * @return List of zero-based prefix positions in the given arguments string. */ private static List findAllPrefixPositions(String argsString, Prefix... prefixes) { return Arrays.stream(prefixes) @@ -80,9 +80,9 @@ private static int findPrefixPosition(String argsString, String prefix, int from * extracted prefixes to their respective arguments. Prefixes are extracted based on their zero-based positions in * {@code argsString}. * - * @param argsString Arguments string of the form: {@code preamble value value ...} - * @param prefixPositions Zero-based positions of all prefixes in {@code argsString} - * @return ArgumentMultimap object that maps prefixes to their arguments + * @param argsString Arguments string of the form: {@code preamble value value ...}. + * @param prefixPositions Zero-based positions of all prefixes in {@code argsString}. + * @return ArgumentMultimap object that maps prefixes to their arguments. */ private static ArgumentMultimap extractArguments(String argsString, List prefixPositions) { diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index 75b1a9bf119..c3a6d6d7bce 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -1,15 +1,106 @@ package seedu.address.logic.parser; /** - * Contains Command Line Interface (CLI) syntax definitions common to multiple commands + * Contains Command Line Interface (CLI) syntax definitions common to multiple commands. */ public class CliSyntax { /* Prefix definitions */ - public static final Prefix PREFIX_NAME = new Prefix("n/"); - public static final Prefix PREFIX_PHONE = new Prefix("p/"); - public static final Prefix PREFIX_EMAIL = new Prefix("e/"); - public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); - public static final Prefix PREFIX_TAG = new Prefix("t/"); + /** + * Stands for PREFIX_MEMBER. + */ + public static final Prefix PREFIX_MEMBER = new Prefix("-mem/"); + + /** + * Stands for PREFIX_ID. + */ + public static final Prefix PREFIX_ID = new Prefix("-id/"); + + /** + * Stands for PREFIX_INDEX. + */ + public static final Prefix PREFIX_INDEX = new Prefix("-i/"); + + /** + * Stands for PREFIX_NAME. + */ + public static final Prefix PREFIX_NAME = new Prefix("-n/"); + + /** + * Stands for PREFIX_PHONE. + */ + public static final Prefix PREFIX_PHONE = new Prefix("-p/"); + + /** + * Stands for PREFIX_EMAIL. + */ + public static final Prefix PREFIX_EMAIL = new Prefix("-e/"); + + /** + * Stands for PREFIX_ADDRESS. + */ + public static final Prefix PREFIX_ADDRESS = new Prefix("-a/"); + + /** + * Stands for PREFIX_DATE. + */ + public static final Prefix PREFIX_DATE = new Prefix("-d/"); + + /** + * Stands for PREFIX_CREDIT. + */ + public static final Prefix PREFIX_CREDIT = new Prefix("-c/"); + + /** + * Stands for PREFIX_REDEEM. + */ + public static final Prefix PREFIX_REDEEM = new Prefix("-rd/"); + + /** + * Stands for PREFIX_TRANSACTION. + */ + public static final Prefix PREFIX_TRANSACTION = new Prefix("-txn/"); + + /** + * Stands for PREFIX_BILLING. + */ + public static final Prefix PREFIX_BILLING = new Prefix("-b/"); + + /** + * Stands for PREFIX_RESERVATION. + */ + public static final Prefix PREFIX_RESERVATION = new Prefix("-rs/"); + + /** + * Stands for PREFIX_DATE_TIME. + */ + public static final Prefix PREFIX_DATE_TIME = new Prefix("-dt/"); + + /** + * Stands for PREFIX_REMARK. + */ + public static final Prefix PREFIX_REMARK = new Prefix("-rm/"); + + /** + * Stands for PREFIX_PASS. + */ + public static final Prefix PREFIX_PASS = new Prefix("-pass/"); + + /** + * Stands for PREFIX_TAG. + */ + public static final Prefix PREFIX_TAG = new Prefix("-tag/"); + + + /* Only used in sort command */ + /** + * Stands for PREFIX_ASCENDING_ORDER. + */ + public static final Prefix PREFIX_ASC = new Prefix("-a/"); + + /** + * Stands for PREFIX_DESCENDING_ORDER. + */ + public static final Prefix PREFIX_DESC = new Prefix("-d/"); } diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java index 522b93081cc..c091d5add25 100644 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java @@ -1,29 +1,19 @@ package seedu.address.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; - -import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.DeleteCommand; import seedu.address.logic.parser.exceptions.ParseException; /** - * Parses input arguments and creates a new DeleteCommand object + * Parses input arguments and creates a new DeleteCommand object. */ -public class DeleteCommandParser implements Parser { +public abstract class DeleteCommandParser { /** * 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 an DeleteCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format. */ - public DeleteCommand parse(String args) throws ParseException { - try { - Index index = ParserUtil.parseIndex(args); - return new DeleteCommand(index); - } catch (ParseException pe) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe); - } - } + public abstract DeleteCommand parse(String args) throws ParseException; } diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandPrefixParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandPrefixParser.java new file mode 100644 index 00000000000..4d498e482aa --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/DeleteCommandPrefixParser.java @@ -0,0 +1,48 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESERVATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TRANSACTION; + +import seedu.address.logic.commands.DeleteCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Distinguishes between delete member and delete transaction commands and + * returns an DeleteMemberCommandParser or DeleteTransactionCommandParser + * depends on the very first prefix appears after command word. + */ +public class DeleteCommandPrefixParser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteCommandParser + * and returns an DeleteCommandParser object for execution. + * + * @param args the input arguments related delete command to be parsed. + * @return DeleteMemberCommandParser or DeleteTransactionCommandParser or DeleteReservationCommandParser. + * @throws ParseException if the user input does not conform the expected format. + */ + public DeleteCommandParser parse(String args) throws ParseException { + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize( + args, PREFIX_MEMBER, PREFIX_TRANSACTION, PREFIX_RESERVATION); + + if (argMultimap.getValue(PREFIX_MEMBER).isPresent() + && argMultimap.getValue(PREFIX_TRANSACTION).isEmpty() + && argMultimap.getValue(PREFIX_RESERVATION).isEmpty()) { + return new DeleteMemberCommandParser(); + } else if (argMultimap.getValue(PREFIX_TRANSACTION).isPresent() + && argMultimap.getValue(PREFIX_MEMBER).isEmpty() + && argMultimap.getValue(PREFIX_RESERVATION).isEmpty()) { + return new DeleteTransactionCommandParser(); + } else if (argMultimap.getValue(PREFIX_RESERVATION).isPresent() + && argMultimap.getValue(PREFIX_MEMBER).isEmpty() + && argMultimap.getValue(PREFIX_TRANSACTION).isEmpty()) { + return new DeleteReservationCommandParser(); + } else { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/DeleteMemberCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteMemberCommandParser.java new file mode 100644 index 00000000000..7b643ce9492 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/DeleteMemberCommandParser.java @@ -0,0 +1,58 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.DeleteMemberCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.member.Id; + +/** + * Parses input arguments and creates a new DeleteMemberCommand object. + */ +public class DeleteMemberCommandParser extends DeleteCommandParser implements Parser { + + private static final int PREFIX_SIZE = 3; + + /** + * Parses the given {@code String} of arguments in the context of the DeleteMemberCommand + * and returns a DeleteMemberCommand object for execution. + * + * @param args the input arguments related delete member command to be parsed. + * @return DeleteMemberCommand the class for process input delete member command string. + * @throws ParseException if the user input does not conform the expected format. + */ + public DeleteMemberCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_MEMBER, PREFIX_ID, PREFIX_INDEX); + + if (argMultimap.getSize() != PREFIX_SIZE || argMultimap.getValue(PREFIX_MEMBER).isEmpty() + || (argMultimap.getValue(PREFIX_ID).isEmpty() && argMultimap.getValue(PREFIX_INDEX).isEmpty()) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteMemberCommand.MESSAGE_USAGE)); + } + + if (argMultimap.getValue(PREFIX_ID).isPresent()) { + Id id = ParserUtil.parseMemberId(argMultimap.getValue(PREFIX_ID).get()); + return new DeleteMemberCommand(id); + } + + if (argMultimap.getValue(PREFIX_INDEX).isPresent()) { + Index index; + try { + index = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get()); + } catch (ParseException pe) { + throw new ParseException(String.format(pe.getMessage(), DeleteMemberCommand.MESSAGE_USAGE), pe); + } + return new DeleteMemberCommand(index); + } + + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteMemberCommand.MESSAGE_USAGE)); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/DeleteReservationCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteReservationCommandParser.java new file mode 100644 index 00000000000..ae00a9a98ea --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/DeleteReservationCommandParser.java @@ -0,0 +1,52 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESERVATION; + +import java.util.stream.Stream; + +import seedu.address.logic.commands.DeleteReservationCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.reservation.Id; + +/** + * Parses input arguments and creates a new DeleteReservationCommand object. + */ +public class DeleteReservationCommandParser extends DeleteCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the DeleteReservationCommand + * and returns a DeleteReservationCommand object for execution. + * + * @param args the input arguments related delete reservation command to be parsed. + * @return DeleteReservationCommand the class for process input delete reservation command string. + * @throws ParseException if the user input does not conform the expected format. + */ + public DeleteReservationCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_RESERVATION, PREFIX_ID); + + if (!arePrefixesPresent(argMultimap, PREFIX_RESERVATION, PREFIX_ID) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteReservationCommand.MESSAGE_USAGE)); + } + + String ids = argMultimap.getValue(PREFIX_ID).get(); + seedu.address.model.member.Id memberId = + ParserUtil.parseMemberId(ids.substring(0, seedu.address.model.member.Id.LENGTH)); + Id reservationId = + ParserUtil.parseReservationId(ids.substring(seedu.address.model.member.Id.LENGTH)); + + return new DeleteReservationCommand(memberId, reservationId); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/DeleteTransactionCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteTransactionCommandParser.java new file mode 100644 index 00000000000..06214275460 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/DeleteTransactionCommandParser.java @@ -0,0 +1,52 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TRANSACTION; + +import java.util.stream.Stream; + +import seedu.address.logic.commands.DeleteTransactionCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteTransactionCommand object. + */ +public class DeleteTransactionCommandParser extends DeleteCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteTransactionCommand + * and returns a DeleteTransactionCommand object for execution. + * + * @param args the input arguments related delete transaction command to be parsed. + * @return DeleteTransactionCommand the class for process input delete transaction command string. + * @throws ParseException if the user input does not conform the expected format. + */ + public DeleteTransactionCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TRANSACTION, PREFIX_ID); + + if (!arePrefixesPresent(argMultimap, PREFIX_TRANSACTION, PREFIX_ID) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTransactionCommand.MESSAGE_USAGE)); + } + + String ids = argMultimap.getValue(PREFIX_ID).get(); + seedu.address.model.member.Id memberId = + ParserUtil.parseMemberId(ids.substring(0, seedu.address.model.member.Id.LENGTH)); + seedu.address.model.transaction.Id transactionId = + ParserUtil.parseTransactionId(ids.substring(seedu.address.model.member.Id.LENGTH)); + + return new DeleteTransactionCommand(memberId, transactionId); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java index 845644b7dea..dc10f88c52a 100644 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java @@ -1,82 +1,20 @@ package seedu.address.logic.parser; -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_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_TAG; - -import java.util.Collection; -import java.util.Collections; -import java.util.Optional; -import java.util.Set; - -import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.tag.Tag; /** - * Parses input arguments and creates a new EditCommand object + * Parses input arguments and creates a new EditCommandParser object. */ -public class EditCommandParser implements Parser { +public abstract class EditCommandParser { /** * 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 - */ - public EditCommand parse(String args) throws ParseException { - requireNonNull(args); - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); - - Index index; - - try { - index = ParserUtil.parseIndex(argMultimap.getPreamble()); - } catch (ParseException pe) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); - } - - EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); - 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); - - if (!editPersonDescriptor.isAnyFieldEdited()) { - throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); - } - - return new EditCommand(index, 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. + * + * @param args the input arguments related edit member command to be parsed. + * @return EditCommand the class for process input edit command string. + * @throws ParseException if the user input does not conform the expected format. */ - private Optional> parseTagsForEdit(Collection tags) throws ParseException { - assert tags != null; - - if (tags.isEmpty()) { - return Optional.empty(); - } - Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; - return Optional.of(ParserUtil.parseTags(tagSet)); - } - + public abstract EditCommand parse(String args) throws ParseException; } diff --git a/src/main/java/seedu/address/logic/parser/EditCommandPrefixParser.java b/src/main/java/seedu/address/logic/parser/EditCommandPrefixParser.java new file mode 100644 index 00000000000..49bbdd3f77d --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/EditCommandPrefixParser.java @@ -0,0 +1,59 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESERVATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TRANSACTION; + +import seedu.address.commons.status.ExecutionStatus; +import seedu.address.logic.commands.EditCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Distinguishes between edit member and edit transaction commands and + * returns an EditMemberCommandParser or EditTransactionCommandParser + * depends on the very first prefix appears after command word. + */ +public class EditCommandPrefixParser { + private final ExecutionStatus executionStatus; + + /** + * Constructs a {@code EditCommandPrefixParser} with the {@code ExecutionStatus}. + * + * @param executionStatus normal or test. + */ + public EditCommandPrefixParser(ExecutionStatus executionStatus) { + this.executionStatus = executionStatus; + } + + /** + * Parses the given {@code String} of arguments in the context of the EditCommandParser + * and returns an EditCommandParser object for execution. + * + * @param args the input arguments related edit command to be parsed. + * @return EditMemberCommandParser or EditTransactionCommandParser or AddReservationCommandParser. + * @throws ParseException if the user input does not conform the expected format. + */ + public EditCommandParser parse(String args) throws ParseException { + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize( + args, PREFIX_MEMBER, PREFIX_TRANSACTION, PREFIX_RESERVATION); + + if (argMultimap.getValue(PREFIX_MEMBER).isPresent() + && argMultimap.getValue(PREFIX_TRANSACTION).isEmpty() + && argMultimap.getValue(PREFIX_RESERVATION).isEmpty()) { + return new EditMemberCommandParser(); + } else if (argMultimap.getValue(PREFIX_TRANSACTION).isPresent() + && argMultimap.getValue(PREFIX_MEMBER).isEmpty() + && argMultimap.getValue(PREFIX_RESERVATION).isEmpty()) { + return new EditTransactionCommandParser(executionStatus); + } else if (argMultimap.getValue(PREFIX_RESERVATION).isPresent() + && argMultimap.getValue(PREFIX_MEMBER).isEmpty() + && argMultimap.getValue(PREFIX_TRANSACTION).isEmpty()) { + return new EditReservationCommandParser(); + } else { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/EditMemberCommandParser.java b/src/main/java/seedu/address/logic/parser/EditMemberCommandParser.java new file mode 100644 index 00000000000..e439354aaff --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/EditMemberCommandParser.java @@ -0,0 +1,106 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; +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.Set; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.EditMemberCommand; +import seedu.address.logic.commands.EditMemberCommand.EditMemberDescriptor; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.member.Id; +import seedu.address.model.tag.Tag; + +/** + * Parses input arguments and creates a new EditMemberCommand object. + */ +public class EditMemberCommandParser extends EditCommandParser implements Parser { + + private static final int PREFIX_SIZE = 3; + + /** + * Parses the given {@code String} of arguments in the context of the EditMemberCommand + * and returns an EditMemberCommand object for execution. + * + * @param args the input arguments related edit member command to be parsed. + * @return EditMemberCommand the class for process input edit member command string. + * @throws ParseException if the user input does not conform the expected format. + */ + public EditMemberCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_MEMBER, PREFIX_ID, PREFIX_INDEX, + PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + + if (argMultimap.getSize() < PREFIX_SIZE || argMultimap.getValue(PREFIX_MEMBER).isEmpty() + || (argMultimap.getValue(PREFIX_ID).isEmpty() && argMultimap.getValue(PREFIX_INDEX).isEmpty()) + || (argMultimap.getValue(PREFIX_ID).isPresent() && argMultimap.getValue(PREFIX_INDEX).isPresent()) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditMemberCommand.MESSAGE_USAGE)); + } + + EditMemberDescriptor editMemberDescriptor = new EditMemberDescriptor(); + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + editMemberDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); + } + if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { + editMemberDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); + } + if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { + editMemberDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); + } + if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { + editMemberDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); + } + parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editMemberDescriptor::setTags); + + if (!editMemberDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditMemberCommand.MESSAGE_NOT_EDITED); + } + + if (argMultimap.getValue(PREFIX_ID).isPresent()) { + Id id = ParserUtil.parseMemberId(argMultimap.getValue(PREFIX_ID).get()); + return new EditMemberCommand(id, editMemberDescriptor); + } + + if (argMultimap.getValue(PREFIX_INDEX).isPresent()) { + Index index; + try { + index = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get()); + } catch (ParseException pe) { + throw new ParseException(String.format(pe.getMessage(), EditMemberCommand.MESSAGE_USAGE), pe); + } + return new EditMemberCommand(index, editMemberDescriptor); + } + + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditMemberCommand.MESSAGE_USAGE)); + } + + /** + * 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. + */ + private Optional> parseTagsForEdit(Collection tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + return Optional.empty(); + } + 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/EditReservationCommandParser.java b/src/main/java/seedu/address/logic/parser/EditReservationCommandParser.java new file mode 100644 index 00000000000..c439cc872e5 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/EditReservationCommandParser.java @@ -0,0 +1,69 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE_TIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REMARK; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RESERVATION; + +import java.util.stream.Stream; + +import seedu.address.logic.commands.EditReservationCommand; +import seedu.address.logic.commands.EditReservationCommand.EditReservationDescriptor; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EditReservationCommand object. + */ +public class EditReservationCommandParser extends EditCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditCommand + * and returns an EditCommand object for execution. + * + * @param args the input arguments related edit reservation command to be parsed. + * @return EditReservationCommand the class for process input edit reservation command string. + * @throws ParseException if the user input does not conform the expected format. + */ + public EditReservationCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_RESERVATION, PREFIX_ID, + PREFIX_DATE_TIME, PREFIX_REMARK); + + if (!arePrefixesPresent(argMultimap, PREFIX_RESERVATION, PREFIX_ID) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditReservationCommand.MESSAGE_USAGE)); + } + + String ids = argMultimap.getValue(PREFIX_ID).get(); + seedu.address.model.member.Id memberId = + ParserUtil.parseMemberId(ids.substring(0, seedu.address.model.member.Id.LENGTH)); + seedu.address.model.reservation.Id reservationId = + ParserUtil.parseReservationId(ids.substring(seedu.address.model.member.Id.LENGTH)); + + EditReservationDescriptor editReservationDescriptor = new EditReservationDescriptor(); + if (argMultimap.getValue(PREFIX_DATE_TIME).isPresent()) { + editReservationDescriptor.setDateTime( + ParserUtil.parseDateTime(argMultimap.getValue(PREFIX_DATE_TIME).get())); + } + if (argMultimap.getValue(PREFIX_REMARK).isPresent()) { + editReservationDescriptor.setRemark( + ParserUtil.parseRemark(argMultimap.getValue(PREFIX_REMARK).get())); + } + if (!editReservationDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditReservationCommand.MESSAGE_NOT_EDITED); + } + + return new EditReservationCommand(memberId, reservationId, editReservationDescriptor); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/EditTransactionCommandParser.java b/src/main/java/seedu/address/logic/parser/EditTransactionCommandParser.java new file mode 100644 index 00000000000..6d95178a68e --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/EditTransactionCommandParser.java @@ -0,0 +1,79 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_BILLING; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TRANSACTION; + +import java.util.stream.Stream; + +import seedu.address.commons.status.ExecutionStatus; +import seedu.address.commons.util.DateTimeUtil; +import seedu.address.logic.commands.EditTransactionCommand; +import seedu.address.logic.commands.EditTransactionCommand.EditTransactionDescriptor; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.Timestamp; + +/** + * Parses input arguments and creates a new EditTransactionCommand object. + */ +public class EditTransactionCommandParser extends EditCommandParser implements Parser { + + private final ExecutionStatus executionStatus; + + /** + * Constructs a {@code EditTransactionCommandParser} with the given {@code ExecutionStatus}. + */ + public EditTransactionCommandParser(ExecutionStatus executionStatus) { + this.executionStatus = executionStatus; + } + + /** + * Parses the given {@code String} of arguments in the context of the EditTransactionCommand + * and returns an EditTransactionCommand object for execution. + * + * @param args the input arguments related edit transaction command to be parsed. + * @return EditTransactionCommand the class for process input edit transaction command string. + * @throws ParseException if the user input does not conform the expected format. + */ + public EditTransactionCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TRANSACTION, PREFIX_ID, PREFIX_BILLING); + + if (!arePrefixesPresent(argMultimap, PREFIX_TRANSACTION, PREFIX_ID) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditTransactionCommand.MESSAGE_USAGE)); + } + + String ids = argMultimap.getValue(PREFIX_ID).get(); + seedu.address.model.member.Id memberId = + ParserUtil.parseMemberId(ids.substring(0, seedu.address.model.member.Id.LENGTH)); + seedu.address.model.transaction.Id transactionId = + ParserUtil.parseTransactionId(ids.substring(seedu.address.model.member.Id.LENGTH)); + + EditTransactionDescriptor editTransactionDescriptor = new EditTransactionDescriptor(); + if (argMultimap.getValue(PREFIX_BILLING).isPresent()) { + editTransactionDescriptor.setBilling( + ParserUtil.parseBilling(argMultimap.getValue(PREFIX_BILLING).get())); + } + if (!editTransactionDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditTransactionCommand.MESSAGE_NOT_EDITED); + } + Timestamp timestamp = executionStatus == ExecutionStatus.NORMAL + ? ParserUtil.parseTimestamp(DateTimeUtil.generateTimestamp()) + : ParserUtil.parseTimestamp(DateTimeUtil.generateTimestampStub()); + editTransactionDescriptor.setTimestamp(timestamp); + + return new EditTransactionCommand(memberId, transactionId, editTransactionDescriptor); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/EzFoodieParser.java b/src/main/java/seedu/address/logic/parser/EzFoodieParser.java new file mode 100644 index 00000000000..9a8442699f8 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/EzFoodieParser.java @@ -0,0 +1,147 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.exceptions.PermissionException; +import seedu.address.commons.status.ExecutionStatus; +import seedu.address.commons.status.LoginStatus; +import seedu.address.commons.util.CommandUtil; +import seedu.address.logic.commands.AddCommand; +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.ExitCommand; +import seedu.address.logic.commands.FindCommand; +import seedu.address.logic.commands.HelpCommand; +import seedu.address.logic.commands.ListCommand; +import seedu.address.logic.commands.LoginCommand; +import seedu.address.logic.commands.LogoutCommand; +import seedu.address.logic.commands.RedeemCommand; +import seedu.address.logic.commands.SetAccountCommand; +import seedu.address.logic.commands.SortCommand; +import seedu.address.logic.commands.SummaryCommand; +import seedu.address.logic.commands.ViewCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.Model; + +/** + * Parses user input. + */ +public class EzFoodieParser { + + /** + * Used for initial separation of command word and args. + */ + private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + + private final Model model; + private final ExecutionStatus executionStatus; + + /** + * Constructs a {@code EzFoodieParser} with the given {@code Model}. + */ + public EzFoodieParser(Model model) { + this.model = model; + this.executionStatus = ExecutionStatus.NORMAL; + } + + /** + * Constructs a {@code EzFoodieParser} with the given {@code Model} and {@code ExecutionStatus}. + */ + public EzFoodieParser(Model model, ExecutionStatus executionStatus) { + this.model = model; + this.executionStatus = executionStatus; + } + + /** + * 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. + * @throws PermissionException if the user does not have insufficient permission. + */ + public Command parseCommand(String userInput) throws ParseException, PermissionException { + final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); + if (!matcher.matches()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + + CommandUtil.addCommand(userInput); + + final String commandWord = matcher.group("commandWord"); + final String arguments = matcher.group("arguments"); + switch (commandWord) { + + case AddCommand.COMMAND_WORD: + return new AddCommandPrefixParser(model, executionStatus).parse(arguments).parse(arguments); + + case FindCommand.COMMAND_WORD: + return new FindCommandParser().parse(arguments); + + case EditCommand.COMMAND_WORD: + return new EditCommandPrefixParser(executionStatus).parse(arguments).parse(arguments); + + case ViewCommand.COMMAND_WORD: + return new ViewCommandParser().parse(arguments); + + case RedeemCommand.COMMAND_WORD: + return new RedeemCommandParser().parse(arguments); + + case DeleteCommand.COMMAND_WORD: + if (LoginStatus.getLoginStatus() == LoginStatus.MANAGER) { + return new DeleteCommandPrefixParser().parse(arguments).parse(arguments); + } + throw new PermissionException(Messages.MESSAGE_PERMISSION_DENIED); + + case ClearCommand.COMMAND_WORD: + if (LoginStatus.getLoginStatus() == LoginStatus.MANAGER) { + return new ClearCommand(); + } + throw new PermissionException(Messages.MESSAGE_PERMISSION_DENIED); + + case ListCommand.COMMAND_WORD: + return new ListCommandParser().parse(arguments); + + case SortCommand.COMMAND_WORD: + if (LoginStatus.getLoginStatus() == LoginStatus.MANAGER) { + return new SortCommandParser().parse(arguments); + } + throw new PermissionException(Messages.MESSAGE_PERMISSION_DENIED); + + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + + case LoginCommand.COMMAND_WORD: + return new LoginCommandParser().parse(arguments); + + case LogoutCommand.COMMAND_WORD: + return new LogoutCommand(); + + case SetAccountCommand.COMMAND_WORD: + if (LoginStatus.getLoginStatus() == LoginStatus.MANAGER) { + return new SetAccountCommandParser().parse(arguments); + } + throw new PermissionException(Messages.MESSAGE_PERMISSION_DENIED); + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); + + case SummaryCommand.COMMAND_WORD: + if (LoginStatus.getLoginStatus() == LoginStatus.MANAGER) { + return new SummaryCommand(); + } + throw new PermissionException(Messages.MESSAGE_PERMISSION_DENIED); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java index 4fb71f23103..57a4ddf7f09 100644 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java @@ -1,33 +1,103 @@ package seedu.address.logic.parser; import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; import java.util.Arrays; import seedu.address.logic.commands.FindCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.member.EmailContainsKeywordsPredicate; +import seedu.address.model.member.IdContainsKeywordsPredicate; +import seedu.address.model.member.NameContainsKeywordsPredicate; +import seedu.address.model.member.PhoneContainsKeywordsPredicate; +import seedu.address.model.member.RegistrationDateContainsKeywordsPredicate; /** - * Parses input arguments and creates a new FindCommand object + * Parses input arguments and creates a new FindCommand object. */ public class FindCommandParser implements Parser { + private static final int PREFIX_SIZE = 3; + /** * 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 + * + * @param args the input arguments related find command to be parsed. + * @return FindCommand the class for process input find command string. + * @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()) { + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_MEMBER, PREFIX_ID, PREFIX_NAME, + PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_DATE); + + if (argMultimap.getSize() != PREFIX_SIZE || argMultimap.getValue(PREFIX_MEMBER).isEmpty() + || !argMultimap.getPreamble().isEmpty()) { throw new ParseException( String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); } - String[] nameKeywords = trimmedArgs.split("\\s+"); + if (argMultimap.getValue(PREFIX_ID).isPresent()) { + String trimmedArgs = argMultimap.getValue(PREFIX_ID).get().trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + String[] idKeywords = trimmedArgs.split("\\s+"); + return new FindCommand(new IdContainsKeywordsPredicate(Arrays.asList(idKeywords))); + } + + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + String trimmedArgs = argMultimap.getValue(PREFIX_NAME).get().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))); + } + + if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { + String trimmedArgs = argMultimap.getValue(PREFIX_PHONE).get().trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + String[] phoneKeywords = trimmedArgs.split("\\s+"); + return new FindCommand(new PhoneContainsKeywordsPredicate(Arrays.asList(phoneKeywords))); + } + + if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { + String trimmedArgs = argMultimap.getValue(PREFIX_EMAIL).get().trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + String[] emailKeywords = trimmedArgs.split("\\s+"); + return new FindCommand(new EmailContainsKeywordsPredicate(Arrays.asList(emailKeywords))); + } + + if (argMultimap.getValue(PREFIX_DATE).isPresent()) { + String trimmedArgs = argMultimap.getValue(PREFIX_DATE).get().trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + String[] registrationDateKeywords = trimmedArgs.split("\\s+"); + return new FindCommand( + new RegistrationDateContainsKeywordsPredicate(Arrays.asList(registrationDateKeywords))); + } - return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); } } diff --git a/src/main/java/seedu/address/logic/parser/ListCommandParser.java b/src/main/java/seedu/address/logic/parser/ListCommandParser.java new file mode 100644 index 00000000000..4034d70b8da --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ListCommandParser.java @@ -0,0 +1,38 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; + +import seedu.address.logic.commands.ListCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new ListCommand object. + */ +public class ListCommandParser implements Parser { + + private static final int PREFIX_SIZE = 2; + + /** + * Parses the given {@code String} of arguments in the context of the ListCommand + * and returns a ListCommand object for execution. + * + * @param args the input arguments related list command to be parsed. + * @return ListCommand the class for process input list command string. + * @throws ParseException if the user input does not conform the expected format. + */ + public ListCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_MEMBER); + + if (argMultimap.getSize() != PREFIX_SIZE || argMultimap.getValue(PREFIX_MEMBER).isEmpty() + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_USAGE)); + } else { + return new ListCommand(); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/LoginCommandParser.java b/src/main/java/seedu/address/logic/parser/LoginCommandParser.java new file mode 100644 index 00000000000..ac1378aff2e --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/LoginCommandParser.java @@ -0,0 +1,32 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.logic.commands.LoginCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.account.Password; + +/** + * Parses input arguments and creates a new LoginCommand object. + */ +public class LoginCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the LoginCommand + * and returns a LoginCommand object for execution. + * + * @param args the input arguments related login command to be parsed. + * @return LoginCommand the class for process login command string. + * @throws ParseException if the user input does not conform the expected format. + */ + public LoginCommand parse(String args) throws ParseException { + try { + Password password = ParserUtil.parsePassword(args); + return new LoginCommand(password); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, LoginCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/address/logic/parser/Parser.java index d6551ad8e3f..eb6a4cd9553 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..9208ff9d4a8 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -2,29 +2,43 @@ import static java.util.Objects.requireNonNull; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; import seedu.address.commons.core.index.Index; +import seedu.address.commons.util.EncryptUtil; import seedu.address.commons.util.StringUtil; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; +import seedu.address.model.Timestamp; +import seedu.address.model.account.Password; +import seedu.address.model.member.Address; +import seedu.address.model.member.Email; +import seedu.address.model.member.Name; +import seedu.address.model.member.Phone; +import seedu.address.model.member.Point; +import seedu.address.model.reservation.DateTime; +import seedu.address.model.reservation.Remark; import seedu.address.model.tag.Tag; +import seedu.address.model.transaction.Billing; /** * 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."; + public static final String MESSAGE_INVALID_INDEX = + "Index is not a non-zero unsigned integer (range: 1 ~ 2147483647). \n%1$s"; /** * 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). */ public static Index parseIndex(String oneBasedIndex) throws ParseException { @@ -35,6 +49,21 @@ public static Index parseIndex(String oneBasedIndex) throws ParseException { return Index.fromOneBased(Integer.parseInt(trimmedIndex)); } + /** + * Parses a {@code String id} into a {@code Id}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code id} is invalid. + */ + public static seedu.address.model.member.Id parseMemberId(String id) throws ParseException { + requireNonNull(id); + String trimmedId = id.trim(); + if (!seedu.address.model.member.Id.isValidId(trimmedId)) { + throw new ParseException(seedu.address.model.member.Id.MESSAGE_CONSTRAINTS); + } + return new seedu.address.model.member.Id(trimmedId); + } + /** * Parses a {@code String name} into a {@code Name}. * Leading and trailing whitespaces will be trimmed. @@ -80,6 +109,18 @@ public static Address parseAddress(String address) throws ParseException { return new Address(trimmedAddress); } + /** + * Parses a {@code String timestamp} into an {@code Timestamp}. + */ + public static Timestamp parseTimestamp(String timestamp) throws ParseException { + requireNonNull(timestamp); + String trimmedTimestamp = timestamp.trim(); + if (!Timestamp.isValidTimestamp(trimmedTimestamp)) { + throw new ParseException(Timestamp.MESSAGE_CONSTRAINTS); + } + return new Timestamp(trimmedTimestamp); + } + /** * Parses a {@code String email} into an {@code Email}. * Leading and trailing whitespaces will be trimmed. @@ -95,6 +136,83 @@ public static Email parseEmail(String email) throws ParseException { return new Email(trimmedEmail); } + /** + * Parses a {@code String id} into a {@code Id}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code id} is invalid. + */ + public static seedu.address.model.transaction.Id parseTransactionId(String id) throws ParseException { + requireNonNull(id); + String trimmedId = id.trim(); + if (!seedu.address.model.transaction.Id.isValidId(trimmedId)) { + throw new ParseException(seedu.address.model.transaction.Id.MESSAGE_CONSTRAINTS); + } + return new seedu.address.model.transaction.Id(trimmedId); + } + + /** + * Parses a {@code String id} into a {@code Id}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code id} is invalid. + */ + public static seedu.address.model.reservation.Id parseReservationId(String id) throws ParseException { + requireNonNull(id); + String trimmedId = id.trim(); + if (!seedu.address.model.reservation.Id.isValidId(trimmedId)) { + throw new ParseException(seedu.address.model.reservation.Id.MESSAGE_CONSTRAINTS); + } + return new seedu.address.model.reservation.Id(trimmedId); + } + + /** + * Parses a {@code String billing} into a {@code Billing}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code billing} is invalid. + */ + public static Billing parseBilling(String billing) throws ParseException { + requireNonNull(billing); + String trimmedBilling = billing.trim(); + if (!Billing.isValidBilling(trimmedBilling)) { + throw new ParseException(Billing.MESSAGE_CONSTRAINTS); + } + trimmedBilling = BigDecimal.valueOf(Double.parseDouble(trimmedBilling)).stripTrailingZeros() + .setScale(2, RoundingMode.HALF_EVEN).toPlainString(); + return new Billing(trimmedBilling); + } + + /** + * Parses a {@code String dateTime} into a {@code DateTime}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code dateTime} is invalid. + */ + public static DateTime parseDateTime(String dateTime) throws ParseException { + requireNonNull(dateTime); + String trimmedDateTime = dateTime.trim(); + if (!DateTime.isValidDateTime(trimmedDateTime)) { + throw new ParseException(DateTime.MESSAGE_CONSTRAINTS); + } + return new DateTime(trimmedDateTime); + } + + /** + * Parses a {@code String remark} into a {@code Remark}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code remark} is invalid. + */ + public static Remark parseRemark(String remark) throws ParseException { + requireNonNull(remark); + String trimmedRemark = remark.trim(); + if (!Remark.isValidRemark(trimmedRemark)) { + throw new ParseException(Remark.MESSAGE_CONSTRAINTS); + } + return new Remark(trimmedRemark); + } + /** * Parses a {@code String tag} into a {@code Tag}. * Leading and trailing whitespaces will be trimmed. @@ -111,7 +229,10 @@ public static Tag parseTag(String tag) throws ParseException { } /** - * Parses {@code Collection tags} into a {@code Set}. + * 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 Set parseTags(Collection tags) throws ParseException { requireNonNull(tags); @@ -121,4 +242,53 @@ public static Set parseTags(Collection tags) throws ParseException } return tagSet; } + + /** + * Parses a {@code String points} into a {@code Points}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code transaction} is invalid. + */ + public static Point parsePoint(String point) throws ParseException { + requireNonNull(point); + String trimmedPoint = point.trim(); + if (!Point.isValidPoint(trimmedPoint)) { + throw new ParseException(Point.MESSAGE_CONSTRAINTS); + } + return new Point(trimmedPoint); + } + + + /** + * Parses {@code Collection transactions} into a {@code Set}. + */ + public static List parsePoints(Collection points) + throws ParseException { + requireNonNull(points); + final List pointsList = new ArrayList<>(); + for (String pointAmount : points) { + pointsList.add(parsePoint(pointAmount)); + } + return pointsList; + } + + /** + * Parses {@code password} into a {@code Password} and returns it. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code password} is invalid. + */ + public static Password parsePassword(String password) throws ParseException { + String trimmedPassword = password.trim(); + String hashedTrimmedPassword; + try { + hashedTrimmedPassword = EncryptUtil.hash(trimmedPassword); + } catch (NoSuchAlgorithmException e) { + hashedTrimmedPassword = ""; + } + if (!Password.isValidPassword(hashedTrimmedPassword)) { + throw new ParseException(Password.MESSAGE_CONSTRAINTS); + } + return new Password(hashedTrimmedPassword); + } } diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/seedu/address/logic/parser/Prefix.java index c859d5fa5db..ffbd4e8a373 100644 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ b/src/main/java/seedu/address/logic/parser/Prefix.java @@ -2,15 +2,21 @@ /** * A prefix that marks the beginning of an argument in an arguments string. - * E.g. 't/' in 'add James t/ friend'. + * E.g. '-t' in 'add James -t friend'. */ public class Prefix { private final String prefix; + /** + * Constructs a {@code Prefix} with the given {@code String}. + */ public Prefix(String prefix) { this.prefix = prefix; } + /** + * Gets prefix of command. + */ public String getPrefix() { return prefix; } @@ -19,11 +25,17 @@ public String toString() { return getPrefix(); } + /** + * Overrides the hashCode method. + */ @Override public int hashCode() { return prefix == null ? 0 : prefix.hashCode(); } + /** + * Overrides equals method. + */ @Override public boolean equals(Object obj) { if (!(obj instanceof Prefix)) { diff --git a/src/main/java/seedu/address/logic/parser/RedeemCommandParser.java b/src/main/java/seedu/address/logic/parser/RedeemCommandParser.java new file mode 100644 index 00000000000..c44ec23f3a9 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/RedeemCommandParser.java @@ -0,0 +1,59 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REDEEM; + +import java.util.List; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.RedeemCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.member.Id; +import seedu.address.model.member.Point; + +/** + * Parses input arguments and creates a new RedeemPoint object. + */ +public class RedeemCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the RedeemCommand + * and returns an RedeemCommand object for execution. + * + * @param args the input arguments related redeem point command to be parsed. + * @return RedeemCommand the class for process input redeem point command string. + * @throws ParseException if the user input does not conform the expected format. + */ + @Override + public RedeemCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_REDEEM, PREFIX_ID, PREFIX_INDEX); + if ((argMultimap.getValue(PREFIX_ID).isEmpty() && argMultimap.getValue(PREFIX_INDEX).isEmpty()) + || (argMultimap.getValue(PREFIX_ID).isPresent() && argMultimap.getValue(PREFIX_INDEX).isPresent()) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, RedeemCommand.MESSAGE_USAGE)); + } + + if (argMultimap.getValue(PREFIX_ID).isPresent()) { + List pointToRedeemList = ParserUtil.parsePoints(argMultimap.getAllValues(PREFIX_REDEEM)); + Id id = ParserUtil.parseMemberId(argMultimap.getValue(PREFIX_ID).get()); + return new RedeemCommand(pointToRedeemList, id); + } else if (argMultimap.getValue(PREFIX_INDEX).isPresent()) { + List pointToRedeemList = ParserUtil.parsePoints(argMultimap.getAllValues(PREFIX_REDEEM)); + Index index; + try { + index = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get()); + } catch (ParseException pe) { + throw new ParseException(String.format(pe.getMessage(), RedeemCommand.MESSAGE_USAGE), pe); + } + return new RedeemCommand(pointToRedeemList, index); + } else { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, RedeemCommand.MESSAGE_USAGE)); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/SetAccountCommandParser.java b/src/main/java/seedu/address/logic/parser/SetAccountCommandParser.java new file mode 100644 index 00000000000..f2fb7a4ecaf --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/SetAccountCommandParser.java @@ -0,0 +1,36 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PASS; + +import seedu.address.logic.commands.SetAccountCommand; +import seedu.address.logic.commands.SetAccountCommand.EditAccountDescriptor; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EditAccountCommand object. + */ +public class SetAccountCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditAccountCommand + * and returns an EditAccountCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format. + */ + public SetAccountCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PASS); + + if (argMultimap.getValue(PREFIX_PASS).isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, SetAccountCommand.MESSAGE_USAGE)); + } + + EditAccountDescriptor editAccountDescriptor = new EditAccountDescriptor(); + editAccountDescriptor.setPassword(ParserUtil.parsePassword(argMultimap.getValue(PREFIX_PASS).get())); + + return new SetAccountCommand(editAccountDescriptor); + } +} 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..29da508c9aa --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/SortCommandParser.java @@ -0,0 +1,54 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ASC; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CREDIT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DESC; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; + +import seedu.address.commons.status.SortStatus; +import seedu.address.logic.commands.SortCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.member.CreditSortComparator; + +/** + * Parses input arguments and creates a new SortCommand object. + */ +public class SortCommandParser implements Parser { + + private static final int PREFIX_SIZE = 4; + + /** + * Parses the given {@code String} of arguments in the context of the SortCommand + * and returns a SortCommand object for execution. + * + * @param args the input arguments related sort command to be parsed. + * @return SortCommand the class for process input sort command string. + * @throws ParseException if the user input does not conform the expected format. + */ + public SortCommand parse(String args) throws ParseException { + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_MEMBER, PREFIX_CREDIT, PREFIX_ASC, + PREFIX_DESC); + + if (argMultimap.getSize() != PREFIX_SIZE || argMultimap.getValue(PREFIX_MEMBER).isEmpty() + || argMultimap.getValue(PREFIX_CREDIT).isEmpty() + || (argMultimap.getValue(PREFIX_ASC).isEmpty() && argMultimap.getValue(PREFIX_DESC).isEmpty()) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE)); + } + + if (argMultimap.getValue(PREFIX_ASC).isPresent()) { + return new SortCommand(new CreditSortComparator(SortStatus.ASC)); + } + + if (argMultimap.getValue(PREFIX_DESC).isPresent()) { + return new SortCommand(new CreditSortComparator(SortStatus.DESC)); + } + + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE)); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/ViewCommandParser.java b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java new file mode 100644 index 00000000000..4ecad025dda --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java @@ -0,0 +1,50 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEMBER; + +import java.util.Arrays; +import java.util.stream.Stream; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.ViewCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.member.Id; +import seedu.address.model.member.IdContainsKeywordsPredicate; + +public class ViewCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ViewCommand + * and returns a ViewCommand object for execution. + * + * @param args the input arguments related view command to be parsed. + * @return ViewCommand the class for process view command string. + * @throws ParseException if the user input does not conform the expected format. + */ + public ViewCommand parse(String args) throws ParseException { + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_MEMBER, PREFIX_ID); + + if (!arePrefixesPresent(argMultimap, PREFIX_MEMBER, PREFIX_ID) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE)); + } + + Id id = ParserUtil.parseMemberId(argMultimap.getValue(PREFIX_ID).get()); + + String[] idKeywords = new String[]{id.value}; + return new ViewCommand(new IdContainsKeywordsPredicate(Arrays.asList(idKeywords))); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java index 158a1a54c1c..4cdd5ab19d1 100644 --- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java +++ b/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java @@ -6,11 +6,20 @@ * Represents a parse error encountered by a parser. */ public class ParseException extends IllegalValueException { - + /** + * Parses the exception with string message {@code message}. + */ public ParseException(String message) { super(message); } + /** + * Parses the exception with string message {@codec message} + * amd throwable cause {@code cause}. + * + * @param message the related error message. + * @param cause of the main exception. + */ public ParseException(String message, Throwable cause) { super(message, cause); } diff --git a/src/main/java/seedu/address/model/Account.java b/src/main/java/seedu/address/model/Account.java new file mode 100644 index 00000000000..72d8384ba9c --- /dev/null +++ b/src/main/java/seedu/address/model/Account.java @@ -0,0 +1,119 @@ +package seedu.address.model; + +import static java.util.Objects.requireNonNull; + +import java.util.Objects; + +import seedu.address.model.account.Password; +import seedu.address.model.util.SampleDataUtil; + +/** + * Represents an Account. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Account implements ReadOnlyAccount { + + // Data fields + private Password password; + + /** + * Creates a {@code Account} with default values. + */ + public Account() { + password = SampleDataUtil.getDefaultPassword().getPassword(); + } + + /** + * Creates a {@code Account} with the data in {@code account}. + */ + public Account(ReadOnlyAccount account) { + this(); + resetData(account); + } + + /** + * Every field must be present and not null. + */ + public Account(Password password) { + requireNonNull(password); + this.password = password; + } + + /** + * Resets the existing data of this {@code Account} with {@code newAccount}. + */ + public void resetData(ReadOnlyAccount newAccount) { + requireNonNull(newAccount); + setPassword(newAccount.getPassword()); + } + + /** + * Sets the Password by giving {@code password}. + */ + public void setPassword(Password password) { + requireNonNull(password); + this.password = password; + } + + /** + * Gets the password. + * + * @return Password of the account. + */ + @Override + public Password getPassword() { + return password; + } + + /** + * Returns true if both accounts have the same password. + * This defines a weaker notion of equality between two accounts. + */ + public boolean isSamePassword(Account otherAccount) { + if (otherAccount == this) { + return true; + } + + return otherAccount != null + && otherAccount.getPassword().equals(getPassword()); + } + + /** + * Overrides the equals method. + * Returns true if both accounts have the same data fields. + * This defines a stronger notion of equality between two accounts. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof Account)) { + return false; + } + + Account otherAccount = (Account) other; + + return otherAccount.getPassword().equals(getPassword()); + } + + /** + * Overrides the hashcode method. + * Use this method for custom fields hashing instead of implementing your own. + */ + @Override + public int hashCode() { + return Objects.hash(password); + } + + /** + * Overrides the toString method. + * @return String with password value. + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("Password: ").append(getPassword()); + return builder.toString(); + } +} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java deleted file mode 100644 index 1a943a0781a..00000000000 --- a/src/main/java/seedu/address/model/AddressBook.java +++ /dev/null @@ -1,120 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; - -/** - * Wraps all data at the address-book level - * Duplicates are not allowed (by .isSamePerson comparison) - */ -public class AddressBook implements ReadOnlyAddressBook { - - private final UniquePersonList persons; - - /* - * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication - * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html - * - * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication - * among constructors. - */ - { - persons = new UniquePersonList(); - } - - public AddressBook() {} - - /** - * Creates an AddressBook using the Persons in the {@code toBeCopied} - */ - public AddressBook(ReadOnlyAddressBook toBeCopied) { - this(); - resetData(toBeCopied); - } - - //// list overwrite operations - - /** - * Replaces the contents of the person list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - this.persons.setPersons(persons); - } - - /** - * Resets the existing data of this {@code AddressBook} with {@code newData}. - */ - public void resetData(ReadOnlyAddressBook newData) { - requireNonNull(newData); - - setPersons(newData.getPersonList()); - } - - //// person-level operations - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - public boolean hasPerson(Person person) { - requireNonNull(person); - return persons.contains(person); - } - - /** - * Adds a person to the address book. - * The person must not already exist in the address book. - */ - public void addPerson(Person p) { - persons.add(p); - } - - /** - * Replaces the given person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. - */ - public void setPerson(Person target, Person editedPerson) { - requireNonNull(editedPerson); - - persons.setPerson(target, editedPerson); - } - - /** - * Removes {@code key} from this {@code AddressBook}. - * {@code key} must exist in the address book. - */ - public void removePerson(Person key) { - persons.remove(key); - } - - //// util methods - - @Override - public String toString() { - return persons.asUnmodifiableObservableList().size() + " persons"; - // TODO: refine later - } - - @Override - public ObservableList getPersonList() { - return persons.asUnmodifiableObservableList(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddressBook // instanceof handles nulls - && persons.equals(((AddressBook) other).persons)); - } - - @Override - public int hashCode() { - return persons.hashCode(); - } -} diff --git a/src/main/java/seedu/address/model/EzFoodie.java b/src/main/java/seedu/address/model/EzFoodie.java new file mode 100644 index 00000000000..4d001995085 --- /dev/null +++ b/src/main/java/seedu/address/model/EzFoodie.java @@ -0,0 +1,148 @@ +package seedu.address.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; +import java.util.function.Predicate; + +import javafx.collections.ObservableList; +import seedu.address.model.member.Member; +import seedu.address.model.member.UniqueMemberList; + +/** + * Wraps all data at the ezFoodie level. + * Duplicates are not allowed (by .isSameMember comparison). + */ +public class EzFoodie implements ReadOnlyEzFoodie { + + private final UniqueMemberList members; + + /* + * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication + * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html + * + * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * among constructors. + */ + { + members = new UniqueMemberList(); + } + + /** + * Constructs {@code EzFoodie} without input value. + */ + public EzFoodie() {} + + /** + * Constructs an {@code ezFoodie} using the Members in the {@code toBeCopied}. + * + * @param toBeCopied the data is going to creates ezFoodie. + */ + public EzFoodie(ReadOnlyEzFoodie toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// list overwrite operations + + /** + * Replaces the contents of the member list with {@code members}. + * {@code members} must not contain duplicate members. + */ + public void setMembers(List members) { + this.members.setMembers(members); + } + + /** + * Resets the existing data of this {@code EzFoodie} with {@code newData}. + */ + public void resetData(ReadOnlyEzFoodie newData) { + requireNonNull(newData); + + setMembers(newData.getMemberList()); + } + + //// member-level operations + + /** + * Returns true if a member with the same identity as {@code member} exists in the ezFoodie. + */ + public boolean hasMember(Member member) { + requireNonNull(member); + return members.contains(member); + } + + /** + * Returns true if a member with the same identity as {@code member} exists in the filtered ezFoodie. + * {@code predicate} is the filter condition for the filtered ezFoodie. + */ + public boolean hasMember(Member member, Predicate predicate) { + requireNonNull(member); + return members.contains(member, predicate); + } + + /** + * Adds a member to the ezFoodie. + * The member must not already exist in the ezFoodie. + */ + public void addMember(Member p) { + members.add(p); + } + + /** + * Replaces the given member {@code target} in the list with {@code editedMember}. + * {@code target} must exist in the ezFoodie. + * The member identity of {@code editedMember} must not be the same as another existing member in the ezFoodie. + */ + public void setMember(Member target, Member editedMember) { + requireNonNull(editedMember); + members.setMember(target, editedMember); + } + + /** + * Removes {@code key} from this {@code EzFoodie}. + * {@code key} must exist in the ezFoodie. + */ + public void removeMember(Member key) { + members.remove(key); + } + + //// util methods + + /** + * Overrides toString method. + */ + @Override + public String toString() { + return members.asUnmodifiableObservableList().size() + " members"; + // TODO: refine later + } + + /** + * Overrides and gets member list. + * + * @return ObservableList a series list data of members. + */ + @Override + public ObservableList getMemberList() { + return members.asUnmodifiableObservableList(); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EzFoodie // instanceof handles nulls + && members.equals(((EzFoodie) other).members)); + } + + /** + * Overrides the hashCode method. + */ + @Override + public int hashCode() { + return members.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index d54df471c1f..f0de6f559b0 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -1,18 +1,26 @@ package seedu.address.model; import java.nio.file.Path; +import java.util.Comparator; import java.util.function.Predicate; import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; +import seedu.address.model.member.Member; /** * The API of the Model component. */ public interface Model { - /** {@code Predicate} that always evaluate to true */ - Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; + /** + * {@code Predicate} that always evaluate to true. + */ + Predicate PREDICATE_SHOW_ALL_MEMBERS = unused -> true; + + /** + * {@code Comparator} sort members by id asc. + */ + Comparator COMPARATOR_SORT_MEMBERS_BY_ID_ASC = Comparator.comparingLong(m -> m.getId().getLongValue()); /** * Replaces user prefs data with the data in {@code userPrefs}. @@ -35,53 +43,105 @@ public interface Model { void setGuiSettings(GuiSettings guiSettings); /** - * Returns the user prefs' address book file path. + * Returns the user prefs' account path. + */ + Path getAccountFilePath(); + + /** + * Sets the user prefs' account path. + */ + void setAccountFilePath(Path accountFilePath); + + /** + * Returns the user prefs' ezFoodie file path. + */ + Path getEzFoodieFilePath(); + + /** + * Sets the user prefs' ezFoodie file path. + */ + void setEzFoodieFilePath(Path ezFoodieFilePath); + + /** + * Replaces account with the data in {@code account}. */ - Path getAddressBookFilePath(); + void setAccount(ReadOnlyAccount account); /** - * Sets the user prefs' address book file path. + * Returns the account. */ - void setAddressBookFilePath(Path addressBookFilePath); + ReadOnlyAccount getAccount(); /** - * Replaces address book data with the data in {@code addressBook}. + * Replaces ezFoodie data with the data in {@code ezFoodie}. */ - void setAddressBook(ReadOnlyAddressBook addressBook); + void setEzFoodie(ReadOnlyEzFoodie ezFoodie); - /** Returns the AddressBook */ - ReadOnlyAddressBook getAddressBook(); + /** + * Returns the EzFoodie. + */ + ReadOnlyEzFoodie getEzFoodie(); /** - * Returns true if a person with the same identity as {@code person} exists in the address book. + * Returns true if a member with the same identity as {@code member} exists in the ezFoodie. */ - boolean hasPerson(Person person); + boolean hasMember(Member member); /** - * Deletes the given person. - * The person must exist in the address book. + * Returns true if a member with the same identity as {@code member} exists in the filtered ezFoodie. + * {@code predicate} is the filter condition for the filtered ezFoodie. */ - void deletePerson(Person target); + boolean hasMember(Member member, Predicate predicate); /** - * Adds the given person. - * {@code person} must not already exist in the address book. + * Deletes the given member. + * The member must exist in the ezFoodie. */ - void addPerson(Person person); + void deleteMember(Member target); /** - * Replaces the given person {@code target} with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. + * Adds the given member. + * {@code member} must not already exist in the ezFoodie. */ - void setPerson(Person target, Person editedPerson); + void addMember(Member member); - /** Returns an unmodifiable view of the filtered person list */ - ObservableList getFilteredPersonList(); + /** + * Replaces the given member {@code target} with {@code editedMember}. + * {@code target} must exist in the ezFoodie. + * The member identity of {@code editedMember} must not be the same as another existing member in the ezFoodie. + */ + void setMember(Member target, Member editedMember); /** - * Updates the filter of the filtered person list to filter by the given {@code predicate}. + * Returns an unmodifiable view of the sorted or filtered member list. + */ + ObservableList getUpdatedMemberList(); + + /** + * Returns an unmodifiable view of the sorted or filtered member list + * for viewCommand to use only. + */ + ObservableList getUpdatedMemberListForView(); + + /** + * Updates the filter of the filtered member list to filter by the given {@code predicate}. + * + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredMemberList(Predicate predicate); + + /** + * Updates the filter of the filtered member list to filter by the given {@code predicate} + * for viewCommand to use only. + * * @throws NullPointerException if {@code predicate} is null. */ - void updateFilteredPersonList(Predicate predicate); + void updateFilteredMemberListForView(Predicate predicate); + + /** + * Updates the sort of the sorted member list to sort by the given {@code comparator}. + * + * @throws NullPointerException if {@code comparator} is null. + */ + void updateSortedMemberList(Comparator comparator); } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 0650c954f5c..9071b4c28e9 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -4,131 +4,275 @@ import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; import java.nio.file.Path; +import java.util.Comparator; import java.util.function.Predicate; import java.util.logging.Logger; import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; +import javafx.collections.transformation.SortedList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; +import seedu.address.model.member.Member; /** - * Represents the in-memory model of the address book data. + * Represents the in-memory model of the ezFoodie data. */ public class ModelManager implements Model { private static final Logger logger = LogsCenter.getLogger(ModelManager.class); - private final AddressBook addressBook; + private final Account account; + private final EzFoodie ezFoodie; private final UserPrefs userPrefs; - private final FilteredList filteredPersons; + private final FilteredList filteredMembers; + private final FilteredList filteredMembersForView; + private final SortedList sortedMembers; /** - * Initializes a ModelManager with the given addressBook and userPrefs. + * Initializes a ModelManager with the given account, ezFoodie and userPrefs. + * Constructs a {@code ModelManager} with input variables {@code ReadOnlyAccount}, + * {@code ReadOnlyEzFoodie} and {@code ReadOnlyUserPrefs}. */ - public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { + public ModelManager(ReadOnlyAccount account, ReadOnlyEzFoodie ezFoodie, ReadOnlyUserPrefs userPrefs) { super(); - requireAllNonNull(addressBook, userPrefs); + requireAllNonNull(account, ezFoodie, userPrefs); - logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); + logger.fine("Initializing with account: " + account + ", + ezFoodie: " + ezFoodie + + " and user prefs " + userPrefs); - this.addressBook = new AddressBook(addressBook); + this.account = new Account(account); + this.ezFoodie = new EzFoodie(ezFoodie); this.userPrefs = new UserPrefs(userPrefs); - filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); + filteredMembers = new FilteredList<>(this.ezFoodie.getMemberList()); + filteredMembersForView = new FilteredList<>(this.ezFoodie.getMemberList()); + sortedMembers = new SortedList<>(filteredMembers); // Wrap the FilteredList in a SortedList + sortedMembers.setComparator(COMPARATOR_SORT_MEMBERS_BY_ID_ASC); } + /** + * Constructs a {@code ModelManager} without any input. + */ public ModelManager() { - this(new AddressBook(), new UserPrefs()); + this(new Account(), new EzFoodie(), new UserPrefs()); } //=========== UserPrefs ================================================================================== + /** + * Replaces user prefs data with the data in {@code userPrefs}. + */ @Override public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { requireNonNull(userPrefs); this.userPrefs.resetData(userPrefs); } + /** + * Returns the user prefs. + */ @Override public ReadOnlyUserPrefs getUserPrefs() { return userPrefs; } + /** + * Returns the user prefs' GUI settings. + */ @Override public GuiSettings getGuiSettings() { return userPrefs.getGuiSettings(); } + /** + * Sets the user prefs' GUI settings. + */ @Override public void setGuiSettings(GuiSettings guiSettings) { requireNonNull(guiSettings); userPrefs.setGuiSettings(guiSettings); } + /** + * Returns the user prefs' account path. + */ + @Override + public Path getAccountFilePath() { + return userPrefs.getAccountFilePath(); + } + + /** + * Sets the user prefs' account path. + */ + @Override + public void setAccountFilePath(Path accountFilePath) { + requireNonNull(accountFilePath); + userPrefs.setAccountFilePath(accountFilePath); + } + + /** + * Returns the user prefs' ezFoodie file path. + */ + @Override + public Path getEzFoodieFilePath() { + return userPrefs.getEzFoodieFilePath(); + } + + /** + * Sets the user prefs' ezFoodie file path. + */ + @Override + public void setEzFoodieFilePath(Path ezFoodieFilePath) { + requireNonNull(ezFoodieFilePath); + userPrefs.setEzFoodieFilePath(ezFoodieFilePath); + } + + //=========== Account ==================================================================================== + + /** + * Replaces account with the data in {@code account}. + */ + @Override + public void setAccount(ReadOnlyAccount account) { + requireNonNull(account); + this.account.resetData(account); + } + + /** + * Returns the Account. + */ @Override - public Path getAddressBookFilePath() { - return userPrefs.getAddressBookFilePath(); + public ReadOnlyAccount getAccount() { + return account; } + //=========== EzFoodie =================================================================================== + + /** + * Replaces ezFoodie data with the data in {@code ezFoodie}. + */ @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - userPrefs.setAddressBookFilePath(addressBookFilePath); + public void setEzFoodie(ReadOnlyEzFoodie ezFoodie) { + this.ezFoodie.resetData(ezFoodie); } - //=========== AddressBook ================================================================================ + /** + * Returns the EzFoodie. + */ + @Override + public ReadOnlyEzFoodie getEzFoodie() { + return ezFoodie; + } + /** + * Returns true if a member with the same identity as {@code member} exists in the ezFoodie. + */ @Override - public void setAddressBook(ReadOnlyAddressBook addressBook) { - this.addressBook.resetData(addressBook); + public boolean hasMember(Member member) { + requireNonNull(member); + return ezFoodie.hasMember(member); } + /** + * Returns true if a member with the same identity as {@code member} exists in the filtered ezFoodie. + * {@code predicate} is the filter condition for the filtered ezFoodie. + */ @Override - public ReadOnlyAddressBook getAddressBook() { - return addressBook; + public boolean hasMember(Member member, Predicate predicate) { + requireNonNull(member); + return ezFoodie.hasMember(member, predicate); } + /** + * Deletes the given member. + * The member must exist in the ezFoodie. + */ @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return addressBook.hasPerson(person); + public void deleteMember(Member target) { + ezFoodie.removeMember(target); } + /** + * Adds the given member. + * {@code member} must not already exist in the ezFoodie. + */ @Override - public void deletePerson(Person target) { - addressBook.removePerson(target); + public void addMember(Member member) { + ezFoodie.addMember(member); + updateFilteredMemberList(PREDICATE_SHOW_ALL_MEMBERS); } + /** + * Replaces the given member {@code target} with {@code editedMember}. + * {@code target} must exist in the ezFoodie. + * The member identity of {@code editedMember} must not be the same as another existing member in the ezFoodie. + */ @Override - public void addPerson(Person person) { - addressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + public void setMember(Member target, Member editedMember) { + requireAllNonNull(target, editedMember); + + ezFoodie.setMember(target, editedMember); } + //=========== Updated Member List for display ============================================================ + + /** + * Returns an unmodifiable view of the list of {@code Member} backed by the internal list of + * {@code versionedEzFoodie}. + */ @Override - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); + public ObservableList getUpdatedMemberList() { + return sortedMembers; + } - addressBook.setPerson(target, editedPerson); + /** + * Returns an unmodifiable view of the list of {@code Member} backed by the internal list of + * {@code versionedEzFoodie} for viewCommand to use only. + */ + @Override + public ObservableList getUpdatedMemberListForView() { + return filteredMembersForView; } - //=========== Filtered Person List Accessors ============================================================= + //=========== Filtered Member List Accessors ============================================================= /** - * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of - * {@code versionedAddressBook} + * Updates the filter of the filtered member list to filter by the given {@code predicate}. + * + * @throws NullPointerException if {@code predicate} is null. */ @Override - public ObservableList getFilteredPersonList() { - return filteredPersons; + public void updateFilteredMemberList(Predicate predicate) { + requireNonNull(predicate); + filteredMembers.setPredicate(predicate); } + /** + * Updates the filter of the filtered member list to filter by the given {@code predicate}. + * + * @throws NullPointerException if {@code predicate} is null. + */ @Override - public void updateFilteredPersonList(Predicate predicate) { + public void updateFilteredMemberListForView(Predicate predicate) { requireNonNull(predicate); - filteredPersons.setPredicate(predicate); + filteredMembersForView.setPredicate(predicate); } + //=========== Sorted Member List Accessors =============================================================== + + /** + * Updates the sort of the sorted member list to sort by the given {@code comparator}. + * + * @throws NullPointerException if {@code comparator} is null. + */ + @Override + public void updateSortedMemberList(Comparator comparator) { + requireNonNull(comparator); + sortedMembers.setComparator(comparator); + } + + /** + * Override the equals method. + */ @Override public boolean equals(Object obj) { // short circuit if same object @@ -143,9 +287,10 @@ public boolean equals(Object obj) { // state check ModelManager other = (ModelManager) obj; - return addressBook.equals(other.addressBook) + return ezFoodie.equals(other.ezFoodie) && userPrefs.equals(other.userPrefs) - && filteredPersons.equals(other.filteredPersons); + && filteredMembers.equals(other.filteredMembers) + && sortedMembers.equals(other.sortedMembers); } } diff --git a/src/main/java/seedu/address/model/ReadOnlyAccount.java b/src/main/java/seedu/address/model/ReadOnlyAccount.java new file mode 100644 index 00000000000..709fd913072 --- /dev/null +++ b/src/main/java/seedu/address/model/ReadOnlyAccount.java @@ -0,0 +1,12 @@ +package seedu.address.model; + +import seedu.address.model.account.Password; + +/** + * Unmodifiable view of an account. + */ +public interface ReadOnlyAccount { + + Password getPassword(); + +} diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java deleted file mode 100644 index 6ddc2cd9a29..00000000000 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ /dev/null @@ -1,17 +0,0 @@ -package seedu.address.model; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; - -/** - * Unmodifiable view of an address book - */ -public interface ReadOnlyAddressBook { - - /** - * Returns an unmodifiable view of the persons list. - * This list will not contain any duplicate persons. - */ - ObservableList getPersonList(); - -} diff --git a/src/main/java/seedu/address/model/ReadOnlyEzFoodie.java b/src/main/java/seedu/address/model/ReadOnlyEzFoodie.java new file mode 100644 index 00000000000..397a46e8bd9 --- /dev/null +++ b/src/main/java/seedu/address/model/ReadOnlyEzFoodie.java @@ -0,0 +1,17 @@ +package seedu.address.model; + +import javafx.collections.ObservableList; +import seedu.address.model.member.Member; + +/** + * Unmodifiable view of an ezFoodie. + */ +public interface ReadOnlyEzFoodie { + + /** + * Returns an unmodifiable view of the members list. + * This list will not contain any duplicate members. + */ + ObservableList getMemberList(); + +} diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java index befd58a4c73..3025a3c0688 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java @@ -11,6 +11,6 @@ public interface ReadOnlyUserPrefs { GuiSettings getGuiSettings(); - Path getAddressBookFilePath(); + Path getEzFoodieFilePath(); } diff --git a/src/main/java/seedu/address/model/Timestamp.java b/src/main/java/seedu/address/model/Timestamp.java new file mode 100644 index 00000000000..2540bc20d0c --- /dev/null +++ b/src/main/java/seedu/address/model/Timestamp.java @@ -0,0 +1,87 @@ +package seedu.address.model; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a timestamp in the ezFoodie. + * Guarantees: immutable; is valid as declared in {@link #isValidTimestamp(String)}. + */ +public class Timestamp { + + /** + * Stands for timestamp message constraints. + */ + public static final String MESSAGE_CONSTRAINTS = + "Timestamps should only contain digits and can be parsed to long, and it should not be blank"; + + /** + * Stands for validation regex of timestamp. + */ + public static final String VALIDATION_REGEX = "[\\p{Digit}]*"; + + /** + * Stands for the timestamp value. + */ + public final String value; + + /** + * Constructs a {@code Timestamp} with input {@code timestamp}. + */ + public Timestamp(String timestamp) { + requireNonNull(timestamp); + checkArgument(isValidTimestamp(timestamp), MESSAGE_CONSTRAINTS); + value = timestamp; + } + + /** + * Returns whether is valid timestamp. + * + * @param test input string test. + * @return boolean true if a given string is a valid timestamp. + */ + public static boolean isValidTimestamp(String test) { + try { + Long.parseLong(test); + return test.matches(VALIDATION_REGEX); + } catch (NumberFormatException e) { + return false; + } + } + + /** + * Returns long value of timestamp. + */ + public Long getLongValue() { + return Long.parseLong(value); + } + + /** + * Overrides the toString method. + * + * @return String of the timestamp value. + */ + @Override + public String toString() { + return value; + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Timestamp // instanceof handles nulls + && value.equals(((Timestamp) other).value)); // state check + } + + /** + * Overrides the hashCode method. + */ + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java index 25a5fd6eab9..5ac5fdf7085 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/address/model/UserPrefs.java @@ -14,15 +14,16 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path accountFilePath = Paths.get("data" , "account.json"); + private Path ezFoodieFilePath = Paths.get("data" , "ezfoodie.json"); /** - * Creates a {@code UserPrefs} with default values. + * Constructs a {@code UserPrefs} with default values. */ public UserPrefs() {} /** - * Creates a {@code UserPrefs} with the prefs in {@code userPrefs}. + * Constructs a {@code UserPrefs} with the prefs in {@code userPrefs}. */ public UserPrefs(ReadOnlyUserPrefs userPrefs) { this(); @@ -35,27 +36,61 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) { public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); - setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setEzFoodieFilePath(newUserPrefs.getEzFoodieFilePath()); } + /** + * Gets Gui Settings. + */ public GuiSettings getGuiSettings() { return guiSettings; } + /** + * Sets the Gui Settings by this input {@code guiSettings}. + */ public void setGuiSettings(GuiSettings guiSettings) { requireNonNull(guiSettings); this.guiSettings = guiSettings; } - public Path getAddressBookFilePath() { - return addressBookFilePath; + /** + * Gets account file path. + * + * @return Path of account file. + */ + public Path getAccountFilePath() { + return accountFilePath; + } + + /** + * Sets the account file path by this input {@code accountFilePath}. + */ + public void setAccountFilePath(Path accountFilePath) { + requireNonNull(accountFilePath); + this.accountFilePath = accountFilePath; + } + + /** + * Gets EzFoodie file path. + * + * @return Path of EzFoodie file. + */ + public Path getEzFoodieFilePath() { + return ezFoodieFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - this.addressBookFilePath = addressBookFilePath; + /** + * Sets the EzFoodie file path by this input {@code ezFoodieFilePath}. + */ + public void setEzFoodieFilePath(Path ezFoodieFilePath) { + requireNonNull(ezFoodieFilePath); + this.ezFoodieFilePath = ezFoodieFilePath; } + /** + * Overrides the equals method. + */ @Override public boolean equals(Object other) { if (other == this) { @@ -68,19 +103,29 @@ public boolean equals(Object other) { UserPrefs o = (UserPrefs) other; return guiSettings.equals(o.guiSettings) - && addressBookFilePath.equals(o.addressBookFilePath); + && accountFilePath.equals(o.accountFilePath) + && ezFoodieFilePath.equals(o.ezFoodieFilePath); } + /** + * Overrides the hashCode method. + */ @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, accountFilePath, ezFoodieFilePath); } + /** + * Overrides toString method. + * + * @return String with gui Setting and file path and location. + */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Gui Settings : " + guiSettings); - sb.append("\nLocal data file location : " + addressBookFilePath); + sb.append("\nLocal account file location : " + accountFilePath); + sb.append("\nLocal data file location : " + ezFoodieFilePath); return sb.toString(); } diff --git a/src/main/java/seedu/address/model/account/Password.java b/src/main/java/seedu/address/model/account/Password.java new file mode 100644 index 00000000000..35be4085bc0 --- /dev/null +++ b/src/main/java/seedu/address/model/account/Password.java @@ -0,0 +1,77 @@ +package seedu.address.model.account; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a password for manager to login. + * Guarantees: immutable; is valid as declared in {@link #isValidPassword(String)}. + */ +public class Password { + + /** + * Stands for default password. + */ + public static final String DEFAULT_PLAINTEXT_PASSWORD = "123456"; + /** + * Stands for password message constraints. + */ + public static final String MESSAGE_CONSTRAINTS = + "Passwords should only contain 32 alphanumeric characters or empty, and it should not be blank"; + /** + * Stands for validation regex. + */ + public static final String VALIDATION_REGEX = "[\\p{Alnum}]*"; + + /** + * Stands for password length. + */ + public static final int LENGTH = 32; + + public final String value; + + /** + * Constructs a {@code Password}. + * + * @param password a valid password. + */ + public Password(String password) { + requireNonNull(password); + checkArgument(isValidPassword(password), MESSAGE_CONSTRAINTS); + value = password; + } + + /** + * Returns true if a given string is a valid password. + */ + public static boolean isValidPassword(String test) { + return test.isEmpty() || (test.matches(VALIDATION_REGEX) && test.length() == LENGTH); + } + + /** + * Overrides toString method. + */ + @Override + public String toString() { + return value; + } + + /** + * Overrides equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Password // instanceof handles nulls + && value.equals(((Password) other).value)); // state check + } + + /** + * Overrides hashCode method. + */ + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/member/Address.java similarity index 72% rename from src/main/java/seedu/address/model/person/Address.java rename to src/main/java/seedu/address/model/member/Address.java index 60472ca22a0..4b67d3ff0f2 100644 --- a/src/main/java/seedu/address/model/person/Address.java +++ b/src/main/java/seedu/address/model/member/Address.java @@ -1,18 +1,21 @@ -package seedu.address.model.person; +package seedu.address.model.member; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's address in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} + * Represents a Member's address in the ezFoodie. + * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)}. */ public class Address { + /** + * Stands for address message constraints. + */ public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank"; - /* - * The first character of the address must not be a whitespace, + /** + * Stands for 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 = "[^\\s].*"; @@ -22,7 +25,7 @@ public class Address { /** * Constructs an {@code Address}. * - * @param address A valid address. + * @param address a valid address. */ public Address(String address) { requireNonNull(address); @@ -37,11 +40,19 @@ public static boolean isValidAddress(String test) { return test.matches(VALIDATION_REGEX); } + /** + * Overrides toString method. + * + * @return String the value of valid address. + */ @Override public String toString() { return value; } + /** + * Overrides the equals method. + */ @Override public boolean equals(Object other) { return other == this // short circuit if same object @@ -49,6 +60,9 @@ public boolean equals(Object other) { && value.equals(((Address) other).value)); // state check } + /** + * Overrides the hashCode method. + */ @Override public int hashCode() { return value.hashCode(); diff --git a/src/main/java/seedu/address/model/member/Credit.java b/src/main/java/seedu/address/model/member/Credit.java new file mode 100644 index 00000000000..4f15068d60a --- /dev/null +++ b/src/main/java/seedu/address/model/member/Credit.java @@ -0,0 +1,108 @@ +package seedu.address.model.member; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Member's id in the ezFoodie. + * Guarantees: immutable; is valid as declared in {@link #isValidCredit(String)}. + */ +public class Credit { + + /** + * Stands for the max credit number. + */ + public static final int MAX = 99999999; + + /** + * Stands for credits message constraints. + */ + public static final String MESSAGE_CONSTRAINTS = + "Credits should only contain no more than 8 digits and it should not be blank, and max credit is " + MAX; + + /** + * Stands for trim leading zero regex + */ + public static final String TRIM_LEADING_ZERO_REGEX = "^0+(?!$)"; + + /** + * Stands for validation regex. + */ + public static final String VALIDATION_REGEX = "[\\p{Digit}]*"; + + /** + * Stands for the length of credit. + */ + public static final int LENGTH = 8; // Max credit is 99999999 + + /** + * Stands for the credit value. + */ + public final String value; + + /** + * Constructs a {@code Credit}. + * + * @param credit a valid credit. + */ + public Credit(String credit) { + requireNonNull(credit); + checkArgument(isValidCredit(credit), MESSAGE_CONSTRAINTS); + value = credit; + } + + /** + * Returns true if a given string is a valid credit. + */ + public static boolean isValidCredit(String test) { + test = test.replaceFirst(TRIM_LEADING_ZERO_REGEX, ""); + try { + return test.length() <= LENGTH && Integer.parseInt(test) <= MAX && test.matches(VALIDATION_REGEX); + } catch (NumberFormatException e) { + return false; + } + } + + /** + * Gets int value of credit. + */ + public int getIntValue() { + return Integer.parseInt(value); + } + + /** + * Gets String value of credit for Point use. + * + * @return String of credit value. + */ + public String getStringValue() { + return value; + } + + /** + * Overrides toString method. + */ + @Override + public String toString() { + return value; + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Credit // instanceof handles nulls + && value.equals(((Credit) other).value)); // state check + } + + /** + * Overrides the hashCode method. + */ + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/member/CreditSortComparator.java b/src/main/java/seedu/address/model/member/CreditSortComparator.java new file mode 100644 index 00000000000..cc10c78e290 --- /dev/null +++ b/src/main/java/seedu/address/model/member/CreditSortComparator.java @@ -0,0 +1,52 @@ +package seedu.address.model.member; + +import java.util.Comparator; + +import seedu.address.commons.status.SortStatus; + +/** + * Compares that the credits between 2 members. + */ +public class CreditSortComparator implements Comparator { + + private final SortStatus sortStatus; + + /** + * Constructs a {@code CreditSortComparator} with input {@code SortStatus}. + */ + public CreditSortComparator(SortStatus sortStatus) { + this.sortStatus = sortStatus; + } + + /** + * Gets sort status. + * + * @return SortStatus the status of sorting whether ASC or DESC. + */ + public SortStatus getSortStatus() { + return sortStatus; + } + + /** + * Overrides the compare method. + */ + @Override + public int compare(Member m1, Member m2) { + if (sortStatus == SortStatus.DESC) { + return Integer.compare(m2.getCredit().getIntValue(), m1.getCredit().getIntValue()); + } else { + return Integer.compare(m1.getCredit().getIntValue(), m2.getCredit().getIntValue()); + } + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof CreditSortComparator // instanceof handles nulls + && sortStatus.equals(((CreditSortComparator) other).sortStatus)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/member/Email.java similarity index 83% rename from src/main/java/seedu/address/model/person/Email.java rename to src/main/java/seedu/address/model/member/Email.java index f866e7133de..34a23096e67 100644 --- a/src/main/java/seedu/address/model/person/Email.java +++ b/src/main/java/seedu/address/model/member/Email.java @@ -1,15 +1,19 @@ -package seedu.address.model.person; +package seedu.address.model.member; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's email in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} + * Represents a Member's email in the ezFoodie. + * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)}. */ public class Email { private static final String SPECIAL_CHARACTERS = "+_.-"; + + /** + * Stands for message constraints for email. + */ public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain " + "and adhere to the following constraints:\n" + "1. The local-part should only contain alphanumeric characters and these special characters, excluding " @@ -29,14 +33,21 @@ public class Email { + "(-" + ALPHANUMERIC_NO_UNDERSCORE + ")*"; private static final String DOMAIN_LAST_PART_REGEX = "(" + DOMAIN_PART_REGEX + "){2,}$"; // At least two chars private static final String DOMAIN_REGEX = "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX; + + /** + * Stands for validation regex should includes @ in between. + */ public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX; + /** + * Stands for email string. + */ public final String value; /** * Constructs an {@code Email}. * - * @param email A valid email address. + * @param email a valid email address. */ public Email(String email) { requireNonNull(email); @@ -51,11 +62,19 @@ public static boolean isValidEmail(String test) { return test.matches(VALIDATION_REGEX); } + /** + * Overrides the toString method. + * + * @return String of the valid email. + */ @Override public String toString() { return value; } + /** + * Overrides the equals method. + */ @Override public boolean equals(Object other) { return other == this // short circuit if same object @@ -63,6 +82,9 @@ public boolean equals(Object other) { && value.equals(((Email) other).value)); // state check } + /** + * Overrides the hashCode method. + */ @Override public int hashCode() { return value.hashCode(); diff --git a/src/main/java/seedu/address/model/member/EmailContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/member/EmailContainsKeywordsPredicate.java new file mode 100644 index 00000000000..9e74ab99812 --- /dev/null +++ b/src/main/java/seedu/address/model/member/EmailContainsKeywordsPredicate.java @@ -0,0 +1,41 @@ +package seedu.address.model.member; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; + +/** + * Tests that a {@code Member}'s {@code Email} matches any of the keywords given. + */ +public class EmailContainsKeywordsPredicate implements Predicate { + private final List keywords; + + /** + * Constructs {@code EmailContainsKeywordsPredicate} with input {@code keywords}. + */ + public EmailContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + /** + * Overrides the test method. + * Evaluates this predicate on the given argument. + */ + @Override + public boolean test(Member member) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(member.getEmail().value, keyword)); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EmailContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((EmailContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/member/Id.java b/src/main/java/seedu/address/model/member/Id.java new file mode 100644 index 00000000000..aeee489a6be --- /dev/null +++ b/src/main/java/seedu/address/model/member/Id.java @@ -0,0 +1,106 @@ +package seedu.address.model.member; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Member's id in the ezFoodie. + * Guarantees: immutable; is valid as declared in {@link #isValidId(String)}. + */ +public class Id { + + /** + * Stands for the max Id number. + */ + public static final int MAX = 99999; + + /** + * Stands for Id message constraints. + */ + public static final String MESSAGE_CONSTRAINTS = + "Member IDs should only contain 5 digits and it should not be blank, and max ID is " + MAX; + + /** + * Stands for trim leading zero regex + */ + public static final String TRIM_LEADING_ZERO_REGEX = "^0+(?!$)"; + + /** + * Stands for validation regex of Id. + */ + public static final String VALIDATION_REGEX = "[\\p{Digit}]*"; + + /** + * Stands for pattern of Id. + */ + public static final String PATTERN = "%05d"; + + /** + * Stands for length of Id is five. + */ + public static final int LENGTH = 5; // Max ID is 99999 + + /** + * Stands Id value. + */ + public final String value; + + /** + * Constructs a {@code Id}. + * + * @param id a valid id. + */ + public Id(String id) { + requireNonNull(id); + checkArgument(isValidId(id), MESSAGE_CONSTRAINTS); + value = String.format(PATTERN, Long.parseLong(id)); + } + + /** + * Returns true if a given string is a valid id. + */ + public static boolean isValidId(String test) { + test = test.replaceFirst(TRIM_LEADING_ZERO_REGEX, ""); + try { + return test.length() <= LENGTH && Long.parseLong(test) <= MAX && test.matches(VALIDATION_REGEX); + } catch (NumberFormatException e) { + return false; + } + } + + /** + * Returns long value of id. + */ + public Long getLongValue() { + return Long.parseLong(value); + } + + /** + * Overrides the toString method. + * + * @return String of valid Id value. + */ + @Override + public String toString() { + return value; + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Id // instanceof handles nulls + && value.equals(((Id) other).value)); // state check + } + + /** + * Overrides the hashCode method. + */ + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/member/IdContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/member/IdContainsKeywordsPredicate.java new file mode 100644 index 00000000000..994ad05ea2b --- /dev/null +++ b/src/main/java/seedu/address/model/member/IdContainsKeywordsPredicate.java @@ -0,0 +1,41 @@ +package seedu.address.model.member; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; + +/** + * Tests that a {@code Member}'s {@code Id} matches any of the keywords given. + */ +public class IdContainsKeywordsPredicate implements Predicate { + private final List keywords; + + /** + * Constructs a {@code IdContainsKeywordsPredicate} with input {@code keywords}. + */ + public IdContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + /** + * Overrides the test method. + * Evaluates this predicate on the given argument. + */ + @Override + public boolean test(Member member) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(member.getId().value, keyword)); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof IdContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((IdContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/member/Member.java b/src/main/java/seedu/address/model/member/Member.java new file mode 100644 index 00000000000..a57b2977ef6 --- /dev/null +++ b/src/main/java/seedu/address/model/member/Member.java @@ -0,0 +1,308 @@ +package seedu.address.model.member; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import seedu.address.model.Timestamp; +import seedu.address.model.reservation.Reservation; +import seedu.address.model.tag.Tag; +import seedu.address.model.transaction.Transaction; + +/** + * Represents a Member in the ezFoodie. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Member { + + // Identity fields + private final Id id; + private final Name name; + private final Phone phone; + private final Email email; + + // Data fields + private final Address address; + private final Timestamp timestamp; + private final Credit credit; + private final Point point; + private final Set tags = new HashSet<>(); + + private final List transactions = new ArrayList<>(); + private final List reservations = new ArrayList<>(); + + + /** + * Constructs {@code Member} with follow parameters. + * Every field must be present and not null. + * + * @param id the member id + * @param name the member name + * @param phone the member phone + * @param email the member email + * @param address the member address + * @param timestamp the member timestamp + * @param credit the member credits + * @param point the member point + * @param transactions the member transactions + * @param reservations the member reservations + * @param tags the member tag + */ + + public Member(Id id, Name name, Phone phone, Email email, Address address, + Timestamp timestamp, Credit credit, Point point, List transactions, + List reservations, Set tags) { + requireAllNonNull(id, name, phone, email, address, timestamp, credit, point, transactions, reservations, tags); + this.id = id; + this.name = name; + this.phone = phone; + this.email = email; + this.address = address; + this.timestamp = timestamp; + this.credit = credit; + this.point = point; + this.tags.addAll(tags); + this.transactions.addAll(transactions); + this.reservations.addAll(reservations); + } + + /** + * Gets Id. + * + * @return Id the valid member id. + */ + public Id getId() { + return id; + } + + /** + * Gets name. + * + * @return Name the valid member name. + */ + public Name getName() { + return name; + } + + /** + * Gets phone. + * + * @return Phone the valid member phone. + */ + public Phone getPhone() { + return phone; + } + + /** + * Gets email. + * + * @return Email the valid member email. + */ + public Email getEmail() { + return email; + } + + /** + * Gets address. + * + * @return Address the valid member address. + */ + public Address getAddress() { + return address; + } + + /** + * Gets timestamp. + * + * @return Timestamp the valid member timestamp. + */ + public Timestamp getTimestamp() { + return timestamp; + } + + /** + * Gets credit. + * + * @return Credit the valid member credit. + */ + public Credit getCredit() { + return credit; + } + + /** + * Gets point. + * + * @return Point the valid member point. + */ + public Point getPoint() { + return this.point; + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * + * @return Set a series of tags. + */ + public Set getTags() { + return Collections.unmodifiableSet(tags); + } + + /** + * Returns an immutable transaction set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public List getTransactions() { + return Collections.unmodifiableList(transactions); + } + + /** + * Returns an immutable transaction list, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public List getReservations() { + return Collections.unmodifiableList(reservations); + } + + /** + * Returns true if both members have the same id, phone or email. + * This defines a weaker notion of equality between two members. + */ + public boolean isSameMember(Member otherMember) { + if (otherMember == this) { + return true; + } + + return otherMember != null + && (isSameId(otherMember) || isSameEmail(otherMember) || isSamePhone(otherMember)); + } + + /** + * Returns true if both members have the same id. + * This defines a weaker notion of equality between two members. + */ + public boolean isSameId(Member otherMember) { + if (otherMember == this) { + return true; + } + + return otherMember != null + && otherMember.getId().equals(getId()); + } + + /** + * Returns true if both members have the same phone. + * This defines a weaker notion of equality between two members. + */ + public boolean isSamePhone(Member otherMember) { + if (otherMember == this) { + return true; + } + + return otherMember != null + && otherMember.getPhone().equals(getPhone()); + } + + /** + * Returns true if both members have the same email. + * This defines a weaker notion of equality between two members. + */ + public boolean isSameEmail(Member otherMember) { + if (otherMember == this) { + return true; + } + + return otherMember != null + && otherMember.getEmail().equals(getEmail()); + } + + /** + * Returns true if both members have the same identity and data fields. + * This defines a stronger notion of equality between two members. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Member)) { + return false; + } + + Member otherMember = (Member) other; + return otherMember.getId().equals(getId()) + && otherMember.getName().equals(getName()) + && otherMember.getPhone().equals(getPhone()) + && otherMember.getEmail().equals(getEmail()) + && otherMember.getAddress().equals(getAddress()) + && otherMember.getTimestamp().equals(getTimestamp()) + && otherMember.getCredit().equals(getCredit()) + && otherMember.getPoint().equals(getPoint()) + && otherMember.getTransactions().equals(getTransactions()) + && otherMember.getReservations().equals(getReservations()) + && otherMember.getTags().equals(getTags()); + } + + /** + * Overrides the hashCode method. + */ + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(id, name, phone, email, address, timestamp, credit, point, + transactions, reservations, tags); + } + + /** + * Overrides the toString method. + * + * @return String of including all id, name, phone, email, address + * timestamp, credit, point, tag, transaction and reservation. + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("Id: ") + .append(getId()) + .append("; Name: ") + .append(getName()) + .append("; Phone: ") + .append(getPhone()) + .append("; Email: ") + .append(getEmail()) + .append("; Address: ") + .append(getAddress()) + .append("; Timestamp: ") + .append(getTimestamp()) + .append("; Credit: ") + .append(getCredit()) + .append("; Point: ") + .append(getPoint()); + + Set tags = getTags(); + if (!tags.isEmpty()) { + builder.append("; Tags: "); + tags.forEach(builder::append); + } + + List transactions = getTransactions(); + if (!transactions.isEmpty()) { + builder.append("; Transactions: "); + transactions.forEach(builder::append); + } + + List reservations = getReservations(); + if (!reservations.isEmpty()) { + builder.append("; Reservations: "); + reservations.forEach(builder::append); + } + + return builder.toString(); + } +} diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/member/Name.java similarity index 72% rename from src/main/java/seedu/address/model/person/Name.java rename to src/main/java/seedu/address/model/member/Name.java index 79244d71cf7..c1cfc01185a 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/seedu/address/model/member/Name.java @@ -1,29 +1,35 @@ -package seedu.address.model.person; +package seedu.address.model.member; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's name in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} + * Represents a Member's name in the ezFoodie. + * Guarantees: immutable; is valid as declared in {@link #isValidName(String)}. */ public class Name { + /** + * Stands for message constraints of name. + */ public static final String MESSAGE_CONSTRAINTS = "Names should only contain alphanumeric characters and spaces, and it should not be blank"; - /* - * The first character of the address must not be a whitespace, + /** + * Stands for the first character of the name must not be a whitespace, * otherwise " " (a blank string) becomes a valid input. */ public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; + /** + * Stands for full name value. + */ public final String fullName; /** * Constructs a {@code Name}. * - * @param name A valid name. + * @param name a valid name. */ public Name(String name) { requireNonNull(name); @@ -38,12 +44,19 @@ public static boolean isValidName(String test) { return test.matches(VALIDATION_REGEX); } - + /** + * Overrides the toString method. + * + * @return String of a valid name. + */ @Override public String toString() { return fullName; } + /** + * Overrides equals method. + */ @Override public boolean equals(Object other) { return other == this // short circuit if same object @@ -51,6 +64,9 @@ public boolean equals(Object other) { && fullName.equals(((Name) other).fullName)); // state check } + /** + * Overrides hashCode method. + */ @Override public int hashCode() { return fullName.hashCode(); diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/member/NameContainsKeywordsPredicate.java similarity index 63% rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/seedu/address/model/member/NameContainsKeywordsPredicate.java index c9b5868427c..d5b7ee376f1 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/seedu/address/model/member/NameContainsKeywordsPredicate.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.address.model.member; import java.util.List; import java.util.function.Predicate; @@ -6,21 +6,31 @@ import seedu.address.commons.util.StringUtil; /** - * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. + * Tests that a {@code Member}'s {@code Name} matches any of the keywords given. */ -public class NameContainsKeywordsPredicate implements Predicate { +public class NameContainsKeywordsPredicate implements Predicate { private final List keywords; + /** + * Constructs {@code NameContainsKeywordsPredicate} with input {@code List}. + */ public NameContainsKeywordsPredicate(List keywords) { this.keywords = keywords; } + /** + * Overrides the test method. + * Evaluates this predicate on the given argument. + */ @Override - public boolean test(Person person) { + public boolean test(Member member) { return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(member.getName().fullName, keyword)); } + /** + * Overrides the equals method. + */ @Override public boolean equals(Object other) { return other == this // short circuit if same object diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/member/Phone.java similarity index 69% rename from src/main/java/seedu/address/model/person/Phone.java rename to src/main/java/seedu/address/model/member/Phone.java index 872c76b382f..efe3f222e5b 100644 --- a/src/main/java/seedu/address/model/person/Phone.java +++ b/src/main/java/seedu/address/model/member/Phone.java @@ -1,24 +1,24 @@ -package seedu.address.model.person; +package seedu.address.model.member; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's phone number in the address book. + * Represents a Member's phone number in the ezFoodie. * 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,}"; + "Phone numbers should only contain 8 digits, and it should start with 3, 6, 8 or 9"; + public static final String VALIDATION_REGEX = "^[3689]\\d{7}"; public final String value; /** * Constructs a {@code Phone}. * - * @param phone A valid phone number. + * @param phone a valid phone number */ public Phone(String phone) { requireNonNull(phone); @@ -33,11 +33,19 @@ public static boolean isValidPhone(String test) { return test.matches(VALIDATION_REGEX); } + /** + * Overrides toString method. + * + * @return String of the valid phone value + */ @Override public String toString() { return value; } + /** + * Overrides equals method. + */ @Override public boolean equals(Object other) { return other == this // short circuit if same object @@ -45,6 +53,9 @@ public boolean equals(Object other) { && value.equals(((Phone) other).value)); // state check } + /** + * Overrides hashCode method. + */ @Override public int hashCode() { return value.hashCode(); diff --git a/src/main/java/seedu/address/model/member/PhoneContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/member/PhoneContainsKeywordsPredicate.java new file mode 100644 index 00000000000..1717f9d2231 --- /dev/null +++ b/src/main/java/seedu/address/model/member/PhoneContainsKeywordsPredicate.java @@ -0,0 +1,41 @@ +package seedu.address.model.member; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; + +/** + * Tests that a {@code Member}'s {@code Phone} matches any of the keywords given. + */ +public class PhoneContainsKeywordsPredicate implements Predicate { + private final List keywords; + + /** + * Constructs a {@code PhoneContainsKeywordsPredicate} with input {@code keywords}. + */ + public PhoneContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + /** + * Overrides the test method. + * Evaluates this predicate on the given argument. + */ + @Override + public boolean test(Member member) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(member.getPhone().value, keyword)); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof PhoneContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((PhoneContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/member/Point.java b/src/main/java/seedu/address/model/member/Point.java new file mode 100644 index 00000000000..c0b74d134a9 --- /dev/null +++ b/src/main/java/seedu/address/model/member/Point.java @@ -0,0 +1,111 @@ +package seedu.address.model.member; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Point in the ezFoodie which should be equivalent to credit, + * and it can be redeem from a redemption process. + */ +public class Point { + + /** + * Stands for point max value. + */ + public static final int MAX = 99999999; + + /** + * Stands for message constraints for point. + */ + public static final String MESSAGE_CONSTRAINTS = + "Points should only contain no more than 8 digits and it should not be blank, and max point is " + MAX; + + /** + * Stands for trim leading zero regex. + */ + public static final String TRIM_LEADING_ZERO_REGEX = "^0+(?!$)"; + + /** + * Stands for validation regex. + */ + public static final String VALIDATION_REGEX = "[\\p{Digit}]*"; + + /** + * Stands for point max length. + */ + public static final int LENGTH = 8; // Max point is 99999999 + + /** + * Stands for point value. + */ + public final String value; + + /** + * Constructs a {@code Point}. + * + * @param point a valid point. + */ + public Point(String point) { + requireNonNull(point); + checkArgument(isValidPoint(point), MESSAGE_CONSTRAINTS); + value = point; + } + + /** + * Returns true if a given string is a valid point. + */ + public static boolean isValidPoint(String test) { + test = test.replaceFirst(TRIM_LEADING_ZERO_REGEX, ""); + try { + return test.length() <= LENGTH && Integer.parseInt(test) <= MAX && test.matches(VALIDATION_REGEX); + } catch (NumberFormatException e) { + return false; + } + } + + /** + * Gets int value of point. + * + * @return int the value converts from string to int type. + */ + public int getIntValue() { + return Integer.parseInt(value); + } + + /** + * Overrides the toString method. + * + * @return String of point value. + */ + @Override + public String toString() { + return value; + } + + /** + * Returns double value of transaction amount. + * + * @return double the value converts from string to double type. + */ + public double getDoubleValue() { + return Double.parseDouble(value); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Point // instanceof handles nulls + && value.equals(((Point) other).value)); // state check + } + + /** + * Overrides hashCode method. + */ + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/member/RegistrationDateContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/member/RegistrationDateContainsKeywordsPredicate.java new file mode 100644 index 00000000000..8a9345b6549 --- /dev/null +++ b/src/main/java/seedu/address/model/member/RegistrationDateContainsKeywordsPredicate.java @@ -0,0 +1,55 @@ +package seedu.address.model.member; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.DateTimeUtil; + +/** + * Tests that a {@code Member}'s {@code Timestamp} within the given registration dates. + */ +public class RegistrationDateContainsKeywordsPredicate implements Predicate { + + public static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); + + private final List keywords; + + /** + * Constructs a {@code RegistrationDateContainsKeywordsPredicate} with input {@code List}. + * + * @param keywords a list of string keywords. + */ + public RegistrationDateContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + /** + * Overrides the test method. + * Evaluates this predicate on the given argument. + */ + @Override + public boolean test(Member member) { + return keywords.stream().anyMatch(keyword -> { + try { + return DateTimeUtil.isDateContainsTimestamp( + DATE_FORMAT.parse(keyword), Long.parseLong(member.getTimestamp().value)); + } catch (ParseException e) { + e.printStackTrace(); + } + return false; + }); + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof RegistrationDateContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((RegistrationDateContainsKeywordsPredicate) other).keywords)); // state check + } +} diff --git a/src/main/java/seedu/address/model/member/Tier.java b/src/main/java/seedu/address/model/member/Tier.java new file mode 100644 index 00000000000..de42669f83c --- /dev/null +++ b/src/main/java/seedu/address/model/member/Tier.java @@ -0,0 +1,60 @@ +package seedu.address.model.member; + +/** + * Represents a Tier in the ezFoodie. + */ +public enum Tier { + + BRONZE("Bronze", 0), + SILVER("Silver", 100), + GOLD("Gold", 500), + PLATINUM("Platinum", 1000); + + private final String key; + private final Integer value; + + /** + * Constructs a {@code Tier} with input {@code key} and {@code value}. + * + * @param key the different tiers for Platinum, Gold, Silver or Bronze. + * @param value the value of tier. + */ + Tier(String key, Integer value) { + this.key = key; + this.value = value; + } + + /** + * Returns key of tier. + */ + public String getKey() { + return key; + } + + /** + * Returns all possible key of tier. + */ + public static String[] getAllKeys() { + return new String[]{BRONZE.key, SILVER.key, GOLD.key, PLATINUM.key}; + } + + /** + * Returns value of tier. + */ + public int getValue() { + return this.value; + } + + public static String getTierByCredit(int credit) { + if (credit >= PLATINUM.getValue()) { + return PLATINUM.key; + } + if (credit >= GOLD.getValue()) { + return GOLD.key; + } + if (credit >= SILVER.getValue()) { + return SILVER.key; + } + return BRONZE.key; + } +} diff --git a/src/main/java/seedu/address/model/member/UniqueMemberList.java b/src/main/java/seedu/address/model/member/UniqueMemberList.java new file mode 100644 index 00000000000..5b85cddcb12 --- /dev/null +++ b/src/main/java/seedu/address/model/member/UniqueMemberList.java @@ -0,0 +1,163 @@ +package seedu.address.model.member; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.member.exceptions.DuplicateMemberException; +import seedu.address.model.member.exceptions.MemberNotFoundException; + +/** + * A list of members that enforces uniqueness between its elements and does not allow nulls. + * A member is considered unique by comparing using {@code Member#isSameMember(Member)}. As such, adding and updating of + * members uses Member#isSameMember(Member) for equality so as to ensure that the member being added or updated is + * unique in terms of identity in the UniqueMemberList. However, the removal of a member uses Member#equals(Object) so + * as to ensure that the member with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Member#isSameMember(Member) + */ +public class UniqueMemberList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent member as the given argument. + */ + public boolean contains(Member toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameMember); + } + + /** + * Returns true if the filtered list contains an equivalent member as the given argument. + * {@code predicate} is the filter condition for the filtered list. + */ + public boolean contains(Member toCheck, Predicate predicate) { + requireNonNull(toCheck); + return internalList.stream().filter(predicate).anyMatch(toCheck::isSameMember); + } + + /** + * Adds a member to the list. + * The member must not already exist in the list. + */ + public void add(Member toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateMemberException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the member {@code target} in the list with {@code editedMember}. + * {@code target} must exist in the list. + * The member identity of {@code editedMember} must not be the same as another existing member in the list. + */ + public void setMember(Member target, Member editedMember) { + requireAllNonNull(target, editedMember); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new MemberNotFoundException(); + } + + if (!target.isSameMember(editedMember) && contains(editedMember)) { + throw new DuplicateMemberException(); + } + + internalList.set(index, editedMember); + } + + /** + * Removes the equivalent member from the list. + * The member must exist in the list. + */ + public void remove(Member toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new MemberNotFoundException(); + } + } + + /** + * Sets Members by input {@code replacement}. + */ + public void setMembers(UniqueMemberList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code members}. + * {@code members} must not contain duplicate members. + * + * @param members a list of members will be set. + */ + public void setMembers(List members) { + requireAllNonNull(members); + if (!membersAreUnique(members)) { + throw new DuplicateMemberException(); + } + + internalList.setAll(members); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + * + * @return ObservableList a list of observations. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + /** + * Overrides the Iterator method. + */ + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + /** + * Returns true if {@code members} contains only unique members. + */ + private boolean membersAreUnique(List members) { + for (int i = 0; i < members.size() - 1; i++) { + for (int j = i + 1; j < members.size(); j++) { + if (members.get(i).isSameMember(members.get(j))) { + return false; + } + } + } + return true; + } + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueMemberList // instanceof handles nulls + && internalList.equals(((UniqueMemberList) other).internalList)); + } + + /** + * Overrides the hashCode method. + */ + @Override + public int hashCode() { + return internalList.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/member/exceptions/DuplicateMemberException.java b/src/main/java/seedu/address/model/member/exceptions/DuplicateMemberException.java new file mode 100644 index 00000000000..50985b5e1c0 --- /dev/null +++ b/src/main/java/seedu/address/model/member/exceptions/DuplicateMemberException.java @@ -0,0 +1,14 @@ +package seedu.address.model.member.exceptions; + +/** + * Signals that the operation will result in duplicate Members (Members are considered duplicates if they have the same + * identity). + */ +public class DuplicateMemberException extends RuntimeException { + /** + * Constructs a {@code DuplicateMemberException} without input. + */ + public DuplicateMemberException() { + super("Operation would result in duplicate members"); + } +} diff --git a/src/main/java/seedu/address/model/member/exceptions/MemberNotFoundException.java b/src/main/java/seedu/address/model/member/exceptions/MemberNotFoundException.java new file mode 100644 index 00000000000..54c9d021a3d --- /dev/null +++ b/src/main/java/seedu/address/model/member/exceptions/MemberNotFoundException.java @@ -0,0 +1,6 @@ +package seedu.address.model.member.exceptions; + +/** + * Signals that the operation is unable to find the specified member. + */ +public class MemberNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java deleted file mode 100644 index 8ff1d83fe89..00000000000 --- a/src/main/java/seedu/address/model/person/Person.java +++ /dev/null @@ -1,123 +0,0 @@ -package seedu.address.model.person; - -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import seedu.address.model.tag.Tag; - -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. - */ -public class Person { - - // Identity fields - private final Name name; - private final Phone phone; - private final Email email; - - // Data fields - private final Address address; - private final Set tags = new HashSet<>(); - - /** - * Every field must be present and not null. - */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - requireAllNonNull(name, phone, email, address, tags); - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - this.tags.addAll(tags); - } - - public Name getName() { - return name; - } - - public Phone getPhone() { - return phone; - } - - public Email getEmail() { - return email; - } - - public Address getAddress() { - return address; - } - - /** - * Returns an immutable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - */ - public Set getTags() { - return Collections.unmodifiableSet(tags); - } - - /** - * Returns true if both persons have the same name. - * This defines a weaker notion of equality between two persons. - */ - public boolean isSamePerson(Person otherPerson) { - if (otherPerson == this) { - return true; - } - - return otherPerson != null - && otherPerson.getName().equals(getName()); - } - - /** - * Returns true if both persons have the same identity and data fields. - * This defines a stronger notion of equality between two persons. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof Person)) { - return false; - } - - Person otherPerson = (Person) other; - return otherPerson.getName().equals(getName()) - && otherPerson.getPhone().equals(getPhone()) - && otherPerson.getEmail().equals(getEmail()) - && otherPerson.getAddress().equals(getAddress()) - && otherPerson.getTags().equals(getTags()); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append(getName()) - .append("; Phone: ") - .append(getPhone()) - .append("; Email: ") - .append(getEmail()) - .append("; Address: ") - .append(getAddress()); - - Set tags = getTags(); - if (!tags.isEmpty()) { - builder.append("; Tags: "); - tags.forEach(builder::append); - } - return builder.toString(); - } - -} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java deleted file mode 100644 index 0fee4fe57e6..00000000000 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ /dev/null @@ -1,137 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Iterator; -import java.util.List; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; - -/** - * A list of persons that enforces uniqueness between its elements and does not allow nulls. - * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of - * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is - * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so - * as to ensure that the person with exactly the same fields will be removed. - * - * Supports a minimal set of list operations. - * - * @see Person#isSamePerson(Person) - */ -public class UniquePersonList implements Iterable { - - private final ObservableList internalList = FXCollections.observableArrayList(); - private final ObservableList internalUnmodifiableList = - FXCollections.unmodifiableObservableList(internalList); - - /** - * Returns true if the list contains an equivalent person as the given argument. - */ - public boolean contains(Person toCheck) { - requireNonNull(toCheck); - return internalList.stream().anyMatch(toCheck::isSamePerson); - } - - /** - * Adds a person to the list. - * The person must not already exist in the list. - */ - public void add(Person toAdd) { - requireNonNull(toAdd); - if (contains(toAdd)) { - throw new DuplicatePersonException(); - } - internalList.add(toAdd); - } - - /** - * Replaces the person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the list. - * The person identity of {@code editedPerson} must not be the same as another existing person in the list. - */ - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - int index = internalList.indexOf(target); - if (index == -1) { - throw new PersonNotFoundException(); - } - - if (!target.isSamePerson(editedPerson) && contains(editedPerson)) { - throw new DuplicatePersonException(); - } - - internalList.set(index, editedPerson); - } - - /** - * Removes the equivalent person from the list. - * The person must exist in the list. - */ - public void remove(Person toRemove) { - requireNonNull(toRemove); - if (!internalList.remove(toRemove)) { - throw new PersonNotFoundException(); - } - } - - public void setPersons(UniquePersonList replacement) { - requireNonNull(replacement); - internalList.setAll(replacement.internalList); - } - - /** - * Replaces the contents of this list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - requireAllNonNull(persons); - if (!personsAreUnique(persons)) { - throw new DuplicatePersonException(); - } - - internalList.setAll(persons); - } - - /** - * Returns the backing list as an unmodifiable {@code ObservableList}. - */ - public ObservableList asUnmodifiableObservableList() { - return internalUnmodifiableList; - } - - @Override - public Iterator iterator() { - return internalList.iterator(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof UniquePersonList // instanceof handles nulls - && internalList.equals(((UniquePersonList) other).internalList)); - } - - @Override - public int hashCode() { - return internalList.hashCode(); - } - - /** - * Returns true if {@code persons} contains only unique persons. - */ - private boolean personsAreUnique(List persons) { - for (int i = 0; i < persons.size() - 1; i++) { - for (int j = i + 1; j < persons.size(); j++) { - if (persons.get(i).isSamePerson(persons.get(j))) { - return false; - } - } - } - return true; - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java deleted file mode 100644 index d7290f59442..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ /dev/null @@ -1,11 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same - * identity). - */ -public class DuplicatePersonException extends RuntimeException { - public DuplicatePersonException() { - super("Operation would result in duplicate persons"); - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java deleted file mode 100644 index fa764426ca7..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ /dev/null @@ -1,6 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation is unable to find the specified person. - */ -public class PersonNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/reservation/DateTime.java b/src/main/java/seedu/address/model/reservation/DateTime.java new file mode 100644 index 00000000000..b936283bd48 --- /dev/null +++ b/src/main/java/seedu/address/model/reservation/DateTime.java @@ -0,0 +1,83 @@ +package seedu.address.model.reservation; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.time.LocalDateTime; +import java.time.format.DateTimeParseException; + +import seedu.address.commons.util.DateTimeUtil; + +/** + * Represents a Reservation's dateTime in the ezFoodie. + * Guarantees: immutable; is valid as declared in {@link #isValidDateTime(String)}. + */ +public class DateTime { + + /** + * Stands for message constraints of reservation date time. + */ + public static final String MESSAGE_CONSTRAINTS = + "Reservations should be valid date time " + DateTimeUtil.DATE_TIME_PATTERN; + /** + * Stands for reservation value. + */ + public final String value; + + /** + * Constructs a {@code Reservation}. + * + * @param dateTime a valid date time. + */ + public DateTime(String dateTime) { + requireNonNull(dateTime); + checkArgument(isValidDateTime(dateTime), MESSAGE_CONSTRAINTS); + value = dateTime; + } + + /** + * Returns true if a given string is a valid date time. + */ + public static boolean isValidDateTime(String test) { + try { + DateTimeUtil.parseDateTime(test); + return true; + } catch (DateTimeParseException e) { + return false; + } + } + + /** + * Returns LocalDateTime value of date time. + */ + public LocalDateTime getLocalDateTimeValue() { + return DateTimeUtil.parseDateTime(value); + } + + /** + * Overrides the toString method. + */ + @Override + public String toString() { + return value; + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DateTime // instanceof handles nulls + && value.equals(((DateTime) other).value)); // state check + } + + /** + * Overrides the hashCode method. + */ + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/reservation/Id.java b/src/main/java/seedu/address/model/reservation/Id.java new file mode 100644 index 00000000000..1dd2c35bd48 --- /dev/null +++ b/src/main/java/seedu/address/model/reservation/Id.java @@ -0,0 +1,109 @@ +package seedu.address.model.reservation; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Reservation's id in the ezFoodie. + * Guarantees: immutable, is valid as declared in {@link #isValidId(String)}. + */ +public class Id { + + /** + * Stands for the max reservation id number. + */ + public static final int MAX = 999999; + + /** + * Stands for message constraints of reservation member id. + */ + public static final String MESSAGE_CONSTRAINTS = + "Reservation IDs should only contain 6 digits and it should not be blank, and max ID is " + MAX; + + /** + * Stands for trim leading zero regex. + */ + public static final String TRIM_LEADING_ZERO_REGEX = "^0+(?!$)"; + + /** + * Stands for validation regex of reservation id. + */ + public static final String VALIDATION_REGEX = "[\\p{Digit}]*"; + + /** + * Stands for reservation Id pattern. + */ + public static final String PATTERN = "%06d"; + + /** + * Stands for reservation id max length. + */ + public static final int LENGTH = 6; // Max ID is 999999 + + /** + * Stands for reservation id value. + */ + public final String value; + + /** + * Constructs a {@code Id}. + * + * @param id a valid id. + */ + public Id(String id) { + requireNonNull(id); + checkArgument(isValidId(id), MESSAGE_CONSTRAINTS); + value = String.format(PATTERN, Long.parseLong(id)); + } + + /** + * Returns whether is valid id. + * + * @param test input string test. + * @return boolean true if a given string is a valid id. + */ + public static boolean isValidId(String test) { + test = test.replaceFirst(TRIM_LEADING_ZERO_REGEX, ""); + try { + return test.length() <= LENGTH && Long.parseLong(test) <= MAX && test.matches(VALIDATION_REGEX); + } catch (NumberFormatException e) { + return false; + } + } + + /** + * Returns long value of id. + */ + public Long getLongValue() { + return Long.parseLong(value); + } + + /** + * Overrides the toString method. + * + * @return String print reservation id value. + */ + @Override + public String toString() { + return value; + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Id // instanceof handles nulls + && value.equals(((Id) other).value)); // state check + } + + /** + * Overrides the hashCode method. + */ + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/reservation/Remark.java b/src/main/java/seedu/address/model/reservation/Remark.java new file mode 100644 index 00000000000..82d89d8363e --- /dev/null +++ b/src/main/java/seedu/address/model/reservation/Remark.java @@ -0,0 +1,83 @@ +package seedu.address.model.reservation; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Reservation's remark in the Reservation. + * Guarantees: immutable; is valid as declared in {@link #isValidRemark(String)}. + */ +public class Remark { + + public static final String MESSAGE_CONSTRAINTS = "Remarks can take any values, and it should not be blank"; + + /** + * Stands for 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 = "[^\\s].*"; + + /** + * Stands for reservation remark value. + */ + public final String value; + + /** + * Constructs a {@code Remark}. + * + * @param remark a valid remark. + */ + public Remark(String remark) { + requireNonNull(remark); + checkArgument(isValidRemark(remark), MESSAGE_CONSTRAINTS); + value = remark; + } + + /** + * Returns whether is valid remark. + * + * @param test the string input test. + * @return boolean true if a given string is a valid remark. + */ + public static boolean isValidRemark(String test) { + return test.matches(VALIDATION_REGEX); + } + + /** + * Gets double class from a string. + * + * @return double value of remark. + */ + public double getDoubleValue() { + return Double.parseDouble(value); + } + + /** + * Overrides the toString method. + * + * @return String reservation remark value。 + */ + @Override + public String toString() { + return value; + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Remark // instanceof handles nulls + && value.equals(((Remark) other).value)); // state check + } + + /** + * Overrides the hashCode method. + */ + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/reservation/Reservation.java b/src/main/java/seedu/address/model/reservation/Reservation.java new file mode 100644 index 00000000000..476287ad77f --- /dev/null +++ b/src/main/java/seedu/address/model/reservation/Reservation.java @@ -0,0 +1,158 @@ +package seedu.address.model.reservation; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Objects; + +import seedu.address.commons.util.DateTimeUtil; + +/** + * Represents a Reservation in the member. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Reservation { + + public static final String MESSAGE_CONSTRAINTS = + "The given reservation date time should be after current date time."; + + private final Id id; + private final DateTime dateTime; + private final Remark remark; + + /** + * Constructs a {@code Reservation}, + * and every field must be present and not null. + * + * @param id member Id. + * @param dateTime Reservation date time. + * @param remark reservation remark. + */ + public Reservation(Id id, DateTime dateTime, Remark remark) { + requireAllNonNull(id, dateTime, remark); + this.id = id; + this.dateTime = dateTime; + this.remark = remark; + } + + /** + * Gets reservation id. + * + * @return id the reservation member id. + */ + public Id getId() { + return id; + } + + /** + * Gets reservation's date time. + * + * @return DateTime of reservation. + */ + public DateTime getDateTime() { + return dateTime; + } + + /** + * Gets reservation's remark. + * + * @return Remark of reservation. + */ + public Remark getRemark() { + return remark; + } + + /** + * Returns true if both reservations have the same id. + * This defines a weaker notion of equality between two reservations. + */ + public boolean isSameId(Reservation otherReservation) { + if (otherReservation == this) { + return true; + } + + return otherReservation != null + && otherReservation.getId().equals(getId()); + } + + /** + * Returns true if both reservations have the same date. + * This defines a weaker notion of equality between two reservations. + */ + public boolean isSameDate(Reservation otherReservation) { + if (otherReservation == this) { + return true; + } + + LocalDate otherDate = DateTimeUtil.parseDateTime(otherReservation.getDateTime().value).toLocalDate(); + LocalDate date = DateTimeUtil.parseDateTime(getDateTime().value).toLocalDate(); + return otherDate.isEqual(date); + } + + /** + * Returns true if the reservation have the same date with the other date time. + */ + public boolean isSameDate(LocalDateTime otherDateTime) { + + LocalDate otherDate = otherDateTime.toLocalDate(); + LocalDate date = DateTimeUtil.parseDateTime(getDateTime().value).toLocalDate(); + return otherDate.isEqual(date); + } + + /** + * Returns true if a given date time is after current date time. + */ + public static boolean isValidDateTime(DateTime dateTime) { + LocalDateTime otherDateTime = DateTimeUtil.parseDateTime(dateTime.value); + LocalDateTime nowDateTime = LocalDateTime.now(); + return otherDateTime.isAfter(nowDateTime); + } + + /** + * Returns true if both reservations have the same date time and remark. + * This defines a stronger notion of equality between two reservations. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Reservation)) { + return false; + } + + Reservation otherReservation = (Reservation) other; + return otherReservation.getId().equals(getId()) + && otherReservation.getDateTime().equals(getDateTime()) + && otherReservation.getRemark().equals(getRemark()); + } + + /** + * Overrides the hashCode method for custom fields hashing instead of implementing your own. + */ + @Override + public int hashCode() { + return Objects.hash(dateTime, remark); + } + + /** + * Overrides the toString method. + * + * @return String of reservation's information including Id, DateTime and remark. + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("Id: ") + .append(getId()) + .append("; DateTime: ") + .append(getDateTime()) + .append("; Remark: ") + .append(getRemark()); + + return builder.toString(); + } + +} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java index b0ea7e7dad7..f9dea87bd50 100644 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ b/src/main/java/seedu/address/model/tag/Tag.java @@ -4,12 +4,19 @@ import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Tag in the address book. - * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} + * Represents a Tag in the ezFoodie. + * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)}. */ public class Tag { + /** + * Stands for tag message constraints. + */ public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric"; + + /** + * Stands for tag validation regex. + */ public static final String VALIDATION_REGEX = "\\p{Alnum}+"; public final String tagName; @@ -17,7 +24,7 @@ public class Tag { /** * Constructs a {@code Tag}. * - * @param tagName A valid tag name. + * @param tagName a valid tag name. */ public Tag(String tagName) { requireNonNull(tagName); @@ -32,6 +39,9 @@ public static boolean isValidTagName(String test) { return test.matches(VALIDATION_REGEX); } + /** + * Overrides the equals method. + */ @Override public boolean equals(Object other) { return other == this // short circuit if same object @@ -39,13 +49,16 @@ public boolean equals(Object other) { && tagName.equals(((Tag) other).tagName)); // state check } + /** + * Overrides the hashCode method. + */ @Override public int hashCode() { return tagName.hashCode(); } /** - * Format state as text for viewing. + * Overrides the toString method and format state as text for viewing. */ public String toString() { return '[' + tagName + ']'; diff --git a/src/main/java/seedu/address/model/transaction/Billing.java b/src/main/java/seedu/address/model/transaction/Billing.java new file mode 100644 index 00000000000..cbbbfc04ac5 --- /dev/null +++ b/src/main/java/seedu/address/model/transaction/Billing.java @@ -0,0 +1,104 @@ +package seedu.address.model.transaction; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Transaction's billing in the Transaction. + * Guarantees: immutable; is valid as declared in {@link #isValidBilling(String)}. + */ +public class Billing { + + /** + * Stands for the max transaction billing number. + */ + public static final double MAX = 9999.99; + + /** + * Stands for message constraints of transaction billing. + */ + public static final String MESSAGE_CONSTRAINTS = + "Billings should be non-negative numeric with 2 decimal places, and max amount is " + MAX; + + /** + * Stands for trim leading zero regex. + */ + public static final String TRIM_LEADING_ZERO_REGEX = "^0+(?!$)"; + + /** + * Stands for validation regex of transaction billing. + */ + public static final String VALIDATION_REGEX = "\\d*\\.\\d{2}$"; + + /** + * Stands for transaction billing max amount length. + */ + public static final int LENGTH = 7; // Max amount is 9999.99 + + /** + * Stands for transaction billing value. + */ + public final String value; + + /** + * Constructs a {@code Billing}. + * + * @param billing a valid billing amount. + */ + public Billing(String billing) { + requireNonNull(billing); + checkArgument(isValidBilling(billing), MESSAGE_CONSTRAINTS); + value = billing; + } + + /** + * Returns whether is valid billing. + * + * @param test input string test. + * @return boolean true if a given string is a valid billing amount. + */ + public static boolean isValidBilling(String test) { + test = test.replaceFirst(TRIM_LEADING_ZERO_REGEX, ""); + try { + return test.length() <= LENGTH && Double.parseDouble(test) <= MAX && test.matches(VALIDATION_REGEX); + } catch (NumberFormatException e) { + return false; + } + } + + /** + * Returns double value of billing amount. + */ + public double getDoubleValue() { + return Double.parseDouble(value); + } + + /** + * Overrides toString method. + * + * @return transaction billing value. + */ + @Override + public String toString() { + return value; + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Billing // instanceof handles nulls + && value.equals(((Billing) other).value)); // state check + } + + /** + * Overrides the hashCode method. + */ + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/transaction/Id.java b/src/main/java/seedu/address/model/transaction/Id.java new file mode 100644 index 00000000000..ae0620be8d5 --- /dev/null +++ b/src/main/java/seedu/address/model/transaction/Id.java @@ -0,0 +1,106 @@ +package seedu.address.model.transaction; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Transaction's id in the ezFoodie. + * Guarantees: immutable; is valid as declared in {@link #isValidId(String)}. + */ +public class Id { + + /** + * Stands for the max transaction max id number. + */ + public static final int MAX = 999999; + + /** + * Stands for message constraints of transaction Id. + */ + public static final String MESSAGE_CONSTRAINTS = + "Transaction IDs should only contain 6 digits and it should not be blank, and max ID is " + MAX; + + /** + * Stands for trim leading zero regex. + */ + public static final String TRIM_LEADING_ZERO_REGEX = "^0+(?!$)"; + + /** + * Stands for validation regex of transaction Id. + */ + public static final String VALIDATION_REGEX = "[\\p{Digit}]*"; + + /** + * Stands for transaction Id pattern. + */ + public static final String PATTERN = "%06d"; + + /** + * Stands for transaction Id max length. + */ + public static final int LENGTH = 6; // Max ID is 999999 + + /** + * Stands for transaction Id value. + */ + public final String value; + + /** + * Constructs a {@code Id}. + * + * @param id a valid id. + */ + public Id(String id) { + requireNonNull(id); + checkArgument(isValidId(id), MESSAGE_CONSTRAINTS); + value = String.format(PATTERN, Long.parseLong(id)); + } + + /** + * Returns true if a given string is a valid id. + */ + public static boolean isValidId(String test) { + test = test.replaceFirst(TRIM_LEADING_ZERO_REGEX, ""); + try { + return test.length() <= LENGTH && Long.parseLong(test) <= MAX && test.matches(VALIDATION_REGEX); + } catch (NumberFormatException e) { + return false; + } + } + + /** + * Returns long value of id. + */ + public Long getLongValue() { + return Long.parseLong(value); + } + + /** + * Overrides the toString method. + * + * @return transaction billing value. + */ + @Override + public String toString() { + return value; + } + + /** + * Overrides the equals method. + */ + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Id // instanceof handles nulls + && value.equals(((Id) other).value)); // state check + } + + /** + * Overrides the hashCode method. + */ + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/transaction/Transaction.java b/src/main/java/seedu/address/model/transaction/Transaction.java new file mode 100644 index 00000000000..59b8ae0d1f5 --- /dev/null +++ b/src/main/java/seedu/address/model/transaction/Transaction.java @@ -0,0 +1,144 @@ +package seedu.address.model.transaction; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Calendar; +import java.util.Objects; + +import seedu.address.model.Timestamp; + +/** + * Represents a Transaction in the member. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Transaction { + + private final Id id; + private final Timestamp timestamp; + private final Billing billing; + + /** + * Constructs a {@code Transaction}, + * every field must be present and not null. + * + * @param id member Id. + * @param timestamp transaction timestamp. + * @param billing billing details. + */ + public Transaction(Id id, Timestamp timestamp, Billing billing) { + requireAllNonNull(id, timestamp, billing); + this.id = id; + this.timestamp = timestamp; + this.billing = billing; + } + + /** + * Gets transaction id. + * + * @return id the transaction member id. + */ + public Id getId() { + return id; + } + + /** + * Gets transaction timestamp. + * + * @return id the transaction timestamp. + */ + public Timestamp getTimestamp() { + return timestamp; + } + + /** + * Gets transaction billing. + * + * @return id the transaction billing. + */ + public Billing getBilling() { + return billing; + } + + /** + * Returns whether is same id between other transactions. + * This defines a weaker notion of equality between two transactions. + */ + public boolean isSameId(Transaction otherTransaction) { + if (otherTransaction == this) { + return true; + } + + return otherTransaction != null + && otherTransaction.getId().equals(getId()); + } + + /** + * Returns true if a given timestamp is within {@code numOfPastMonths}. + */ + public static boolean isWithinPastMonths(long timestamp, int numOfPastMonths) { + Calendar currentCalendar = Calendar.getInstance(); + currentCalendar.setTimeInMillis(System.currentTimeMillis()); + int currentYear = currentCalendar.get(Calendar.YEAR); + int currentMonth = currentCalendar.get(Calendar.MONTH); + + Calendar calendarToCheck = Calendar.getInstance(); + calendarToCheck.setTimeInMillis(timestamp); + int yearToCheck = calendarToCheck.get(Calendar.YEAR); + int monthToCheck = calendarToCheck.get(Calendar.MONTH); + + int diffYears = currentYear - yearToCheck; + int diffMonths = diffYears * 12 + currentMonth - monthToCheck; + + return diffMonths <= numOfPastMonths; + } + + /** + * Overrides the equals method. + * + * Returns true if both transactions have the same timestamp and billing. + * This defines a stronger notion of equality between two transactions. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Transaction)) { + return false; + } + + Transaction otherTransaction = (Transaction) other; + return otherTransaction.getId().equals(getId()) + && otherTransaction.getTimestamp().equals(getTimestamp()) + && otherTransaction.getBilling().equals(getBilling()); + } + + /** + * Overrides the hashCode method. + */ + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(timestamp, billing); + } + + /** + * Overrides the toString method. + * + * @return String of transaction's information including Id, timestamp and billing. + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("Id: ") + .append(getId()) + .append("; Timestamp: ") + .append(getTimestamp()) + .append("; Billing: ") + .append(getBilling()); + + return builder.toString(); + } + +} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 1806da4facf..3a9be2018f7 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -1,51 +1,145 @@ package seedu.address.model.util; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; +import seedu.address.commons.util.EncryptUtil; +import seedu.address.model.Account; +import seedu.address.model.EzFoodie; +import seedu.address.model.ReadOnlyAccount; +import seedu.address.model.ReadOnlyEzFoodie; +import seedu.address.model.Timestamp; +import seedu.address.model.account.Password; +import seedu.address.model.member.Address; +import seedu.address.model.member.Credit; +import seedu.address.model.member.Email; +import seedu.address.model.member.Member; +import seedu.address.model.member.Name; +import seedu.address.model.member.Phone; +import seedu.address.model.member.Point; +import seedu.address.model.reservation.DateTime; +import seedu.address.model.reservation.Remark; +import seedu.address.model.reservation.Reservation; import seedu.address.model.tag.Tag; +import seedu.address.model.transaction.Billing; +import seedu.address.model.transaction.Transaction; /** - * Contains utility methods for populating {@code AddressBook} with sample data. + * Contains utility methods for populating {@code EzFoodie} with sample data. */ public class SampleDataUtil { - public static Person[] getSamplePersons() { - return new Person[] { - new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), - new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), - new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), - new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), - new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), - new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) + public static Member[] getSampleMembers() { + return new Member[] { + new Member(new seedu.address.model.member.Id("00001"), new Name("Alex Yeoh"), new Phone("87438807"), + new Email("alexyeoh@example.com"), new Address("Blk 30 Geylang Street 29, #06-40"), + new Timestamp("1609545600000"), new Credit("90"), new Point("90"), + getTransactionList(new Transaction(new seedu.address.model.transaction.Id("000001"), + new Timestamp("1609545600000"), new Billing("90.00"))), + getReservationList(new Reservation( + new seedu.address.model.reservation.Id("000001"), new DateTime("2021-01-20 00:00"), + new Remark("2 people"))), + getTagSet()), + new Member(new seedu.address.model.member.Id("00002"), + new Name("Bernice Yu"), new Phone("99272758"), + new Email("berniceyu@example.com"), new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), + new Timestamp("1609549200000"), new Credit("525"), new Point("525"), + getTransactionList( + new Transaction(new seedu.address.model.transaction.Id("000001"), + new Timestamp("1611104400000"), new Billing("200.12")), + new Transaction(new seedu.address.model.transaction.Id("000002"), + new Timestamp("1611190800000"), new Billing("325.30"))), + getReservationList( + new Reservation(new seedu.address.model.reservation.Id("000001"), + new DateTime("2021-01-20 01:00"), new Remark("2 people")), + new Reservation(new seedu.address.model.reservation.Id("000002"), + new DateTime("2021-01-21 01:00"), new Remark("3 people"))), + getTagSet()), + new Member(new seedu.address.model.member.Id("00003"), + new Name("Charlotte Oliveiro"), new Phone("93210283"), + new Email("charlotte@example.com"), new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), + new Timestamp("1609552800000"), new Credit("150"), new Point("150"), + getTransactionList(new Transaction(new seedu.address.model.transaction.Id("000001"), + new Timestamp("1611108000000"), new Billing("150.00"))), + getReservationList(new Reservation(new seedu.address.model.reservation.Id("000001"), + new DateTime("2021-01-20 02:00"), new Remark("2 people"))), + getTagSet()), + new Member(new seedu.address.model.member.Id("00004"), + new Name("David Li"), new Phone("91031282"), + new Email("lidavid@example.com"), new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), + new Timestamp("1609632000000"), new Credit("401"), new Point("401"), + getTransactionList( + new Transaction(new seedu.address.model.transaction.Id("000001"), + new Timestamp("1611111600000"), new Billing("100.50")), + new Transaction(new seedu.address.model.transaction.Id("000002"), + new Timestamp("1611198000000"), new Billing("200.50")), + new Transaction(new seedu.address.model.transaction.Id("000003"), + new Timestamp("1611284400000"), new Billing("100.00"))), + getReservationList( + new Reservation(new seedu.address.model.reservation.Id("000001"), + new DateTime("2021-01-20 03:00"), new Remark("2 people")), + new Reservation(new seedu.address.model.reservation.Id("000002"), + new DateTime("2021-01-21 03:00"), new Remark("3 people")), + new Reservation(new seedu.address.model.reservation.Id("000003"), + new DateTime("2021-01-22 03:00"), new Remark("4 people"))), + getTagSet()), + new Member(new seedu.address.model.member.Id("00005"), + new Name("Irfan Ibrahim"), new Phone("92492021"), + new Email("irfan@example.com"), new Address("Blk 47 Tampines Street 20, #17-35"), + new Timestamp("1609635600000"), new Credit("1010"), new Point("1010"), + getTransactionList( + new Transaction(new seedu.address.model.transaction.Id("000001"), + new Timestamp("1611115200000"), new Billing("1000.00")), + new Transaction(new seedu.address.model.transaction.Id("000002"), + new Timestamp("1611201600000"), new Billing("10.00"))), + getReservationList( + new Reservation(new seedu.address.model.reservation.Id("000001"), + new DateTime("2021-01-20 04:00"), new Remark("2 people")), + new Reservation(new seedu.address.model.reservation.Id("000002"), + new DateTime("2021-01-21 04:00"), new Remark("3 people"))), + getTagSet()), + new Member(new seedu.address.model.member.Id("00006"), + new Name("Roy Balakrishnan"), new Phone("92624417"), + new Email("royb@example.com"), new Address("Blk 45 Aljunied Street 85, #11-31"), + new Timestamp("1609639200000"), new Credit("110"), new Point("110"), + getTransactionList( + new Transaction(new seedu.address.model.transaction.Id("000001"), + new Timestamp("1611118800000"), new Billing("10.00")), + new Transaction(new seedu.address.model.transaction.Id("000002"), + new Timestamp("1611205200000"), new Billing("100.10"))), + getReservationList( + new Reservation(new seedu.address.model.reservation.Id("000001"), + new DateTime("2021-01-20 05:00"), new Remark("2 people")), + new Reservation(new seedu.address.model.reservation.Id("000002"), + new DateTime("2021-01-21 05:00"), new Remark("3 people"))), + getTagSet()) }; } - public static ReadOnlyAddressBook getSampleAddressBook() { - AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); + /** + * Gets default password. + */ + public static ReadOnlyAccount getDefaultPassword() { + Password password; + try { + password = new Password(EncryptUtil.hash(Password.DEFAULT_PLAINTEXT_PASSWORD)); + } catch (NoSuchAlgorithmException e) { + password = new Password(""); + } + return new Account(password); + } + + /** + * Gets Sample EzFoodie. + */ + public static ReadOnlyEzFoodie getSampleEzFoodie() { + EzFoodie sampleEf = new EzFoodie(); + for (Member sampleMember : getSampleMembers()) { + sampleEf.addMember(sampleMember); } - return sampleAb; + return sampleEf; } /** @@ -57,4 +151,18 @@ public static Set getTagSet(String... strings) { .collect(Collectors.toSet()); } + /** + * Returns a reservation list containing the list of reservations given. + */ + public static List getReservationList(Reservation... reservations) { + return Arrays.stream(reservations).collect(Collectors.toList()); + } + + /** + * Returns a transaction list containing the list of transactions given. + */ + public static List getTransactionList(Transaction... transactions) { + return Arrays.stream(transactions).collect(Collectors.toList()); + } + } diff --git a/src/main/java/seedu/address/storage/AccountStorage.java b/src/main/java/seedu/address/storage/AccountStorage.java new file mode 100644 index 00000000000..8d129af1c0d --- /dev/null +++ b/src/main/java/seedu/address/storage/AccountStorage.java @@ -0,0 +1,47 @@ +package seedu.address.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.model.ReadOnlyAccount; + +/** + * Represents a storage for {@link seedu.address.model.Account}. + */ +public interface AccountStorage { + + /** + * Returns the file path of the account file. + */ + Path getAccountFilePath(); + + /** + * Returns Account as a {@link ReadOnlyAccount}. + * Returns {@code Optional.empty()} if storage file is not found. + * + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional readAccount() throws DataConversionException, IOException; + + /** + * @see #getAccountFilePath() + */ + Optional readAccount(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyAccount} to the storage. + * + * @param account cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveAccount(ReadOnlyAccount account) throws IOException; + + /** + * @see #saveAccount(ReadOnlyAccount) + */ + void saveAccount(ReadOnlyAccount account, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/AddressBookStorage.java deleted file mode 100644 index 4599182b3f9..00000000000 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ /dev/null @@ -1,45 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; - -/** - * Represents a storage for {@link seedu.address.model.AddressBook}. - */ -public interface AddressBookStorage { - - /** - * Returns the file path of the data file. - */ - Path getAddressBookFilePath(); - - /** - * Returns AddressBook data as a {@link ReadOnlyAddressBook}. - * Returns {@code Optional.empty()} if storage file is not found. - * @throws DataConversionException if the data in storage is not in the expected format. - * @throws IOException if there was any problem when reading from the storage. - */ - Optional readAddressBook() throws DataConversionException, IOException; - - /** - * @see #getAddressBookFilePath() - */ - Optional readAddressBook(Path filePath) throws DataConversionException, IOException; - - /** - * Saves the given {@link ReadOnlyAddressBook} to the storage. - * @param addressBook cannot be null. - * @throws IOException if there was any problem writing to the file. - */ - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - - /** - * @see #saveAddressBook(ReadOnlyAddressBook) - */ - void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/EzFoodieStorage.java b/src/main/java/seedu/address/storage/EzFoodieStorage.java new file mode 100644 index 00000000000..793bcb1b1cb --- /dev/null +++ b/src/main/java/seedu/address/storage/EzFoodieStorage.java @@ -0,0 +1,47 @@ +package seedu.address.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.model.ReadOnlyEzFoodie; + +/** + * Represents a storage for {@link seedu.address.model.EzFoodie}. + */ +public interface EzFoodieStorage { + + /** + * Returns the file path of the data file. + */ + Path getEzFoodieFilePath(); + + /** + * Returns ezFoodie data as a {@link ReadOnlyEzFoodie}. + * Returns {@code Optional.empty()} if storage file is not found. + * + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional readEzFoodie() throws DataConversionException, IOException; + + /** + * @see #getEzFoodieFilePath() + */ + Optional readEzFoodie(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyEzFoodie} to the storage. + * + * @param ezFoodie cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveEzFoodie(ReadOnlyEzFoodie ezFoodie) throws IOException; + + /** + * @see #saveEzFoodie(ReadOnlyEzFoodie) + */ + void saveEzFoodie(ReadOnlyEzFoodie ezFoodie, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/address/storage/JsonAccountStorage.java b/src/main/java/seedu/address/storage/JsonAccountStorage.java new file mode 100644 index 00000000000..47f7fec9da8 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAccountStorage.java @@ -0,0 +1,101 @@ +package seedu.address.storage; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.commons.util.FileUtil; +import seedu.address.commons.util.JsonUtil; +import seedu.address.model.ReadOnlyAccount; + +/** + * A class to access Account stored as a json file on the hard disk. + */ +public class JsonAccountStorage implements AccountStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonAccountStorage.class); + + private Path filePath; + + /** + * Constructs {@code JsonAccountStorage} with {@code filePath}. + */ + public JsonAccountStorage(Path filePath) { + this.filePath = filePath; + } + + /** + * Gets account file path. + * + * @return Path of account file. + */ + public Path getAccountFilePath() { + return filePath; + } + + /** + * Returns Account as a {@link ReadOnlyAccount}. + * Returns {@code Optional.empty()} if storage file is not found. + * + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + @Override + public Optional readAccount() throws DataConversionException { + return readAccount(filePath); + } + + /** + * Similar to {@link #readAccount()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readAccount(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonAccount = JsonUtil.readJsonFile( + filePath, JsonAdaptedAccount.class); + if (!jsonAccount.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonAccount.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + /** + * Saves the given {@link ReadOnlyAccount} to the storage. + * + * @param account cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + @Override + public void saveAccount(ReadOnlyAccount account) throws IOException { + saveAccount(account, filePath); + } + + /** + * Similar to {@link #saveAccount(ReadOnlyAccount)}. + * + * @param filePath location of the account. Cannot be null. + */ + public void saveAccount(ReadOnlyAccount account, Path filePath) throws IOException { + requireNonNull(account); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonAdaptedAccount(account), filePath); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedAccount.java b/src/main/java/seedu/address/storage/JsonAdaptedAccount.java new file mode 100644 index 00000000000..c043fedd6f5 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedAccount.java @@ -0,0 +1,55 @@ +package seedu.address.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.Account; +import seedu.address.model.ReadOnlyAccount; +import seedu.address.model.account.Password; + +/** + * Jackson-friendly version of {@link Account}. + */ +class JsonAdaptedAccount { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Account's %s is missing!"; + + private final String password; + + /** + * Constructs a {@code JsonAdaptedAccount} with the given account details. + */ + @JsonCreator + public JsonAdaptedAccount(@JsonProperty("password") String password) { + this.password = password; + } + + /** + * Converts a given {@code Account} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonAdaptedAccount}. + */ + public JsonAdaptedAccount(ReadOnlyAccount source) { + this.password = source.getPassword().value; + } + + /** + * Converts this Jackson-friendly adapted account object into the model's {@code Account} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted account. + */ + public Account toModelType() throws IllegalValueException { + if (password == null) { + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, Password.class.getSimpleName())); + } + if (!Password.isValidPassword(password)) { + throw new IllegalValueException(Password.MESSAGE_CONSTRAINTS); + } + final Password modelPassword = new Password(password); + + return new Account(modelPassword); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedMember.java b/src/main/java/seedu/address/storage/JsonAdaptedMember.java new file mode 100644 index 00000000000..701d2c29649 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedMember.java @@ -0,0 +1,196 @@ +package seedu.address.storage; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.Timestamp; +import seedu.address.model.member.Address; +import seedu.address.model.member.Credit; +import seedu.address.model.member.Email; +import seedu.address.model.member.Id; +import seedu.address.model.member.Member; +import seedu.address.model.member.Name; +import seedu.address.model.member.Phone; +import seedu.address.model.member.Point; +import seedu.address.model.reservation.Reservation; +import seedu.address.model.tag.Tag; +import seedu.address.model.transaction.Transaction; + +/** + * Jackson-friendly version of {@link Member}. + */ +class JsonAdaptedMember { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Member's %s field is missing!"; + + private final String id; + private final String name; + private final String phone; + private final String email; + private final String address; + private final String timestamp; + private final String credit; + private final String point; + private final List transactions = new ArrayList<>(); + private final List reservations = new ArrayList<>(); + private final List tagged = new ArrayList<>(); + + /** + * Constructs a {@code JsonAdaptedMember} with the given member details. + */ + @JsonCreator + public JsonAdaptedMember(@JsonProperty("id") String id, @JsonProperty("name") String name, + @JsonProperty("phone") String phone, @JsonProperty("email") String email, + @JsonProperty("address") String address, + @JsonProperty("timestamp") String timestamp, + @JsonProperty("credit") String credit, + @JsonProperty("point") String point, + @JsonProperty("transactions") List transactions, + @JsonProperty("reservations") List reservations, + @JsonProperty("tagged") List tagged) { + + this.id = id; + this.name = name; + this.phone = phone; + this.email = email; + this.address = address; + this.timestamp = timestamp; + this.credit = credit; + this.point = point; + if (tagged != null) { + this.tagged.addAll(tagged); + } + if (transactions != null) { + this.transactions.addAll(transactions); + } + if (reservations != null) { + this.reservations.addAll(reservations); + } + } + + /** + * Converts a given {@code Member} into this class for Jackson use. + */ + public JsonAdaptedMember(Member source) { + id = source.getId().value; + name = source.getName().fullName; + phone = source.getPhone().value; + email = source.getEmail().value; + address = source.getAddress().value; + timestamp = source.getTimestamp().value; + credit = source.getCredit().value; + point = source.getPoint().value; + tagged.addAll(source.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); + transactions.addAll(source.getTransactions().stream() + .map(JsonAdaptedTransaction::new) + .collect(Collectors.toList())); + reservations.addAll(source.getReservations().stream() + .map(JsonAdaptedReservation::new) + .collect(Collectors.toList())); + } + + /** + * Converts this Jackson-friendly adapted member object into the model's {@code Member} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted member. + */ + public Member toModelType() throws IllegalValueException { + final List memberTags = new ArrayList<>(); + for (JsonAdaptedTag tag : tagged) { + memberTags.add(tag.toModelType()); + } + + final List memberTransactions = new ArrayList<>(); + for (JsonAdaptedTransaction transaction : transactions) { + memberTransactions.add(transaction.toModelType()); + } + + final List memberReservations = new ArrayList<>(); + for (JsonAdaptedReservation reservation : reservations) { + memberReservations.add(reservation.toModelType()); + } + + if (id == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Id.class.getSimpleName())); + } + if (!Id.isValidId(id)) { + throw new IllegalValueException(Id.MESSAGE_CONSTRAINTS); + } + final Id modelId = new Id(id); + + if (name == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); + } + if (!Name.isValidName(name)) { + throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); + } + final Name modelName = new Name(name); + + if (phone == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); + } + if (!Phone.isValidPhone(phone)) { + throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); + } + final Phone modelPhone = new Phone(phone); + + if (email == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); + } + if (!Email.isValidEmail(email)) { + throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); + } + final Email modelEmail = new Email(email); + + if (address == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); + } + if (!Address.isValidAddress(address)) { + throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); + } + final Address modelAddress = new Address(address); + + if (timestamp == null) { + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, Timestamp.class.getSimpleName())); + } + if (!Timestamp.isValidTimestamp(timestamp)) { + throw new IllegalValueException(Timestamp.MESSAGE_CONSTRAINTS); + } + final Timestamp modelTimestamp = new Timestamp(timestamp); + + if (credit == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Credit.class.getSimpleName())); + } + if (!Credit.isValidCredit(credit)) { + throw new IllegalValueException(Credit.MESSAGE_CONSTRAINTS); + } + final Credit modelCredit = new Credit(credit); + + if (point == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Point.class.getSimpleName())); + } + if (!Point.isValidPoint(point)) { + throw new IllegalValueException(Point.MESSAGE_CONSTRAINTS); + } + final Point modelPoint = new Point(point); + + final Set modelTags = new HashSet<>(memberTags); + + final List modelTransactions = new ArrayList<>(memberTransactions); + + final List modelReservations = new ArrayList<>(memberReservations); + + return new Member(modelId, modelName, modelPhone, modelEmail, modelAddress, modelTimestamp, modelCredit, + modelPoint, modelTransactions, modelReservations, modelTags); + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java deleted file mode 100644 index a6321cec2ea..00000000000 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ /dev/null @@ -1,109 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Jackson-friendly version of {@link Person}. - */ -class JsonAdaptedPerson { - - 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 final List tagged = new ArrayList<>(); - - /** - * Constructs a {@code JsonAdaptedPerson} with the given person details. - */ - @JsonCreator - public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone, - @JsonProperty("email") String email, @JsonProperty("address") String address, - @JsonProperty("tagged") List tagged) { - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - if (tagged != null) { - this.tagged.addAll(tagged); - } - } - - /** - * Converts a given {@code Person} into this class for Jackson use. - */ - public JsonAdaptedPerson(Person source) { - name = source.getName().fullName; - phone = source.getPhone().value; - email = source.getEmail().value; - address = source.getAddress().value; - tagged.addAll(source.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList())); - } - - /** - * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted person. - */ - public Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); - for (JsonAdaptedTag tag : tagged) { - personTags.add(tag.toModelType()); - } - - if (name == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); - } - if (!Name.isValidName(name)) { - throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); - } - final Name modelName = new Name(name); - - if (phone == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); - } - if (!Phone.isValidPhone(phone)) { - throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); - } - final Phone modelPhone = new Phone(phone); - - if (email == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); - } - if (!Email.isValidEmail(email)) { - throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); - } - final Email modelEmail = new Email(email); - - if (address == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); - } - if (!Address.isValidAddress(address)) { - throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); - } - final Address modelAddress = new Address(address); - - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedReservation.java b/src/main/java/seedu/address/storage/JsonAdaptedReservation.java new file mode 100644 index 00000000000..0d41eebc15f --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedReservation.java @@ -0,0 +1,70 @@ +package seedu.address.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.reservation.DateTime; +import seedu.address.model.reservation.Id; +import seedu.address.model.reservation.Remark; +import seedu.address.model.reservation.Reservation; + +/** + * Jackson-friendly version of {@link Reservation}. + */ +class JsonAdaptedReservation { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Reservation's %s field is missing!"; + + private final String id; + private final String dateTime; + private final String remark; + + /** + * Constructs a {@code JsonAdaptedReservation} with the given reservation details. + */ + @JsonCreator + public JsonAdaptedReservation(@JsonProperty("id") String id, @JsonProperty("dateTime") String dateTime, + @JsonProperty("remark") String remark) { + this.id = id; + this.dateTime = dateTime; + this.remark = remark; + } + + /** + * Constructs a given {@code Reservation} into this class for Jackson use. + */ + public JsonAdaptedReservation(Reservation source) { + id = source.getId().value; + dateTime = source.getDateTime().value; + remark = source.getRemark().value; + } + + /** + * Converts this Jackson-friendly adapted Reservation object into the model's {@code Reservation} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted reservation. + */ + public Reservation toModelType() throws IllegalValueException { + if (id == null) { + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, Id.class.getSimpleName())); + } + final Id modelId = new Id(id); + + if (dateTime == null) { + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, DateTime.class.getSimpleName())); + } + final DateTime modelDateTime = new DateTime(dateTime); + + if (remark == null) { + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, Remark.class.getSimpleName())); + } + final Remark modelRemark = new Remark(remark); + + return new Reservation(modelId, modelDateTime, modelRemark); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/address/storage/JsonAdaptedTag.java index 0df22bdb754..0f7c0c6ecca 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java +++ b/src/main/java/seedu/address/storage/JsonAdaptedTag.java @@ -22,12 +22,17 @@ public JsonAdaptedTag(String tagName) { } /** - * Converts a given {@code Tag} into this class for Jackson use. + * Constructs a given {@code Tag} into this class for Jackson use. */ public JsonAdaptedTag(Tag source) { tagName = source.tagName; } + /** + * Gets tag name. + * + * @return String of tag name. + */ @JsonValue public String getTagName() { return tagName; diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTransaction.java b/src/main/java/seedu/address/storage/JsonAdaptedTransaction.java new file mode 100644 index 00000000000..92f06828e54 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedTransaction.java @@ -0,0 +1,72 @@ +package seedu.address.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.Timestamp; +import seedu.address.model.transaction.Billing; +import seedu.address.model.transaction.Id; +import seedu.address.model.transaction.Transaction; + +/** + * Jackson-friendly version of {@link Transaction}. + */ +class JsonAdaptedTransaction { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Transaction's %s field is missing!"; + + private final String id; + private final String timestamp; + private final String billing; + + /** + * Constructs a {@code JsonAdaptedTransaction} with the given transaction details. + */ + @JsonCreator + public JsonAdaptedTransaction(@JsonProperty("id") String id, + @JsonProperty("timestamp") String timestamp, + @JsonProperty("billing") String billing) { + + this.id = id; + this.timestamp = timestamp; + this.billing = billing; + } + + /** + * Constructs a given {@code Transaction} into this class for Jackson use. + */ + public JsonAdaptedTransaction(Transaction source) { + id = source.getId().value; + timestamp = source.getTimestamp().value; + billing = source.getBilling().value; + } + + /** + * Converts this Jackson-friendly adapted transaction object into the model's {@code Transaction} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted transaction. + */ + public Transaction toModelType() throws IllegalValueException { + if (id == null) { + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, Id.class.getSimpleName())); + } + final Id modelId = new Id(id); + + if (timestamp == null) { + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, Timestamp.class.getSimpleName())); + } + final Timestamp modelTimestamp = new Timestamp(timestamp); + + if (billing == null) { + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, Billing.class.getSimpleName())); + } + final Billing modelBilling = new Billing(billing); + + return new Transaction(modelId, modelTimestamp, modelBilling); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java deleted file mode 100644 index dfab9daaa0d..00000000000 --- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java +++ /dev/null @@ -1,80 +0,0 @@ -package seedu.address.storage; - -import static java.util.Objects.requireNonNull; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; -import java.util.logging.Logger; - -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.FileUtil; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyAddressBook; - -/** - * A class to access AddressBook data stored as a json file on the hard disk. - */ -public class JsonAddressBookStorage implements AddressBookStorage { - - private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class); - - private Path filePath; - - public JsonAddressBookStorage(Path filePath) { - this.filePath = filePath; - } - - public Path getAddressBookFilePath() { - return filePath; - } - - @Override - public Optional readAddressBook() throws DataConversionException { - return readAddressBook(filePath); - } - - /** - * Similar to {@link #readAddressBook()}. - * - * @param filePath location of the data. Cannot be null. - * @throws DataConversionException if the file is not in the correct format. - */ - public Optional readAddressBook(Path filePath) throws DataConversionException { - requireNonNull(filePath); - - Optional jsonAddressBook = JsonUtil.readJsonFile( - filePath, JsonSerializableAddressBook.class); - if (!jsonAddressBook.isPresent()) { - return Optional.empty(); - } - - try { - return Optional.of(jsonAddressBook.get().toModelType()); - } catch (IllegalValueException ive) { - logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); - throw new DataConversionException(ive); - } - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, filePath); - } - - /** - * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}. - * - * @param filePath location of the data. Cannot be null. - */ - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - requireNonNull(addressBook); - requireNonNull(filePath); - - FileUtil.createIfMissing(filePath); - JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonEzFoodieStorage.java b/src/main/java/seedu/address/storage/JsonEzFoodieStorage.java new file mode 100644 index 00000000000..1ba7f88b40e --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonEzFoodieStorage.java @@ -0,0 +1,101 @@ +package seedu.address.storage; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.commons.util.FileUtil; +import seedu.address.commons.util.JsonUtil; +import seedu.address.model.ReadOnlyEzFoodie; + +/** + * A class to access EzFoodie data stored as a json file on the hard disk. + */ +public class JsonEzFoodieStorage implements EzFoodieStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonEzFoodieStorage.class); + + private Path filePath; + + /** + * Constructs a {@code JsonEzFoodieStorage} with the given ezFoodie file path details. + */ + public JsonEzFoodieStorage(Path filePath) { + this.filePath = filePath; + } + + /** + * Gets EzFoodie file path. + * + * @return Path of EzFoodie file. + */ + public Path getEzFoodieFilePath() { + return filePath; + } + + /** + * Returns ezFoodie data as a {@link ReadOnlyEzFoodie}. + * Returns {@code Optional.empty()} if storage file is not found. + * + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + @Override + public Optional readEzFoodie() throws DataConversionException { + return readEzFoodie(filePath); + } + + /** + * Similar to {@link #readEzFoodie()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readEzFoodie(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonEzFoodie = JsonUtil.readJsonFile( + filePath, JsonSerializableEzFoodie.class); + if (!jsonEzFoodie.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonEzFoodie.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + /** + * Saves the given {@link ReadOnlyEzFoodie} to the storage. + * + * @param ezFoodie cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + @Override + public void saveEzFoodie(ReadOnlyEzFoodie ezFoodie) throws IOException { + saveEzFoodie(ezFoodie, filePath); + } + + /** + * Similar to {@link #saveEzFoodie(ReadOnlyEzFoodie)}. + * + * @param filePath location of the data. Cannot be null. + */ + public void saveEzFoodie(ReadOnlyEzFoodie ezFoodie, Path filePath) throws IOException { + requireNonNull(ezFoodie); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableEzFoodie(ezFoodie), filePath); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java deleted file mode 100644 index 5efd834091d..00000000000 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonRootName; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; - -/** - * An Immutable AddressBook that is serializable to JSON format. - */ -@JsonRootName(value = "addressbook") -class JsonSerializableAddressBook { - - public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; - - private final List persons = new ArrayList<>(); - - /** - * Constructs a {@code JsonSerializableAddressBook} with the given persons. - */ - @JsonCreator - public JsonSerializableAddressBook(@JsonProperty("persons") List persons) { - this.persons.addAll(persons); - } - - /** - * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use. - * - * @param source future changes to this will not affect the created {@code JsonSerializableAddressBook}. - */ - public JsonSerializableAddressBook(ReadOnlyAddressBook source) { - persons.addAll(source.getPersonList().stream().map(JsonAdaptedPerson::new).collect(Collectors.toList())); - } - - /** - * Converts this address book into the model's {@code AddressBook} object. - * - * @throws IllegalValueException if there were any data constraints violated. - */ - 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); - } - addressBook.addPerson(person); - } - return addressBook; - } - -} diff --git a/src/main/java/seedu/address/storage/JsonSerializableEzFoodie.java b/src/main/java/seedu/address/storage/JsonSerializableEzFoodie.java new file mode 100644 index 00000000000..2baa9a9c0c0 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonSerializableEzFoodie.java @@ -0,0 +1,62 @@ +package seedu.address.storage; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.EzFoodie; +import seedu.address.model.ReadOnlyEzFoodie; +import seedu.address.model.member.Member; + +/** + * An Immutable EzFoodie that is serializable to JSON format. + */ +@JsonRootName(value = "ezfoodie") +class JsonSerializableEzFoodie { + /** + * Stands for message od duplicated members. + */ + public static final String MESSAGE_DUPLICATE_MEMBER = "Members list contains duplicate member(s)."; + + private final List members = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableEzFoodie} with the given members. + */ + @JsonCreator + public JsonSerializableEzFoodie(@JsonProperty("members") List members) { + this.members.addAll(members); + } + + /** + * Converts a given {@code ReadOnlyEzFoodie} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableEzFoodie}. + */ + public JsonSerializableEzFoodie(ReadOnlyEzFoodie source) { + members.addAll(source.getMemberList().stream().map(JsonAdaptedMember::new).collect(Collectors.toList())); + } + + /** + * Converts this ezFoodie into the model's {@code EzFoodie} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public EzFoodie toModelType() throws IllegalValueException { + EzFoodie ezFoodie = new EzFoodie(); + for (JsonAdaptedMember jsonAdaptedMember : members) { + Member member = jsonAdaptedMember.toModelType(); + if (ezFoodie.hasMember(member)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_MEMBER); + } + ezFoodie.addMember(member); + } + return ezFoodie; + } + +} diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java index bc2bbad84aa..7a2e8ce604e 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java @@ -10,28 +10,42 @@ import seedu.address.model.UserPrefs; /** - * A class to access UserPrefs stored in the hard disk as a json file + * A class to access UserPrefs stored in the hard disk as a json file. */ public class JsonUserPrefsStorage implements UserPrefsStorage { private Path filePath; + /** + * Constructs a {@code JsonUserPrefsStorage} with input {code filePath}. + */ public JsonUserPrefsStorage(Path filePath) { this.filePath = filePath; } + /** + * Returns the file path of the UserPrefs data file. + */ @Override public Path getUserPrefsFilePath() { return filePath; } + /** + * Returns UserPrefs data from storage. + * Returns {@code Optional.empty()} if storage file is not found. + * + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ @Override public Optional readUserPrefs() throws DataConversionException { return readUserPrefs(filePath); } /** - * Similar to {@link #readUserPrefs()} + * Similar to {@link #readUserPrefs()}. + * * @param prefsFilePath location of the data. Cannot be null. * @throws DataConversionException if the file format is not as expected. */ @@ -39,6 +53,12 @@ public Optional readUserPrefs(Path prefsFilePath) throws DataConversi return JsonUtil.readJsonFile(prefsFilePath, UserPrefs.class); } + /** + * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage. + * + * @param userPrefs cannot be null. + * @throws IOException if there was any problem writing to the file. + */ @Override public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { JsonUtil.saveJsonFile(userPrefs, filePath); diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java index beda8bd9f11..4adb56977f4 100644 --- a/src/main/java/seedu/address/storage/Storage.java +++ b/src/main/java/seedu/address/storage/Storage.java @@ -5,28 +5,77 @@ import java.util.Optional; import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyAccount; +import seedu.address.model.ReadOnlyEzFoodie; import seedu.address.model.ReadOnlyUserPrefs; import seedu.address.model.UserPrefs; /** - * API of the Storage component + * API of the Storage component. */ -public interface Storage extends AddressBookStorage, UserPrefsStorage { +public interface Storage extends EzFoodieStorage, AccountStorage, UserPrefsStorage { + /** + * Returns UserPrefs data from storage. + * Returns {@code Optional.empty()} if storage file is not found. + * + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ @Override Optional readUserPrefs() throws DataConversionException, IOException; + /** + * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage. + * + * @param userPrefs cannot be null. + * @throws IOException if there was any problem writing to the file. + */ @Override void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; + /** + * Returns Account as a {@link ReadOnlyAccount}. + * Returns {@code Optional.empty()} if storage file is not found. + * + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ @Override - Path getAddressBookFilePath(); + Optional readAccount() throws DataConversionException, IOException; + /** + * Saves the given {@link ReadOnlyAccount} to the storage. + * + * @param account cannot be null. + * @throws IOException if there was any problem writing to the file. + */ @Override - Optional readAddressBook() throws DataConversionException, IOException; + void saveAccount(ReadOnlyAccount account) throws IOException; + /** + * Returns the file path of the data file. + */ @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; + Path getEzFoodieFilePath(); + + /** + * Returns ezFoodie data as a {@link ReadOnlyEzFoodie}. + * Returns {@code Optional.empty()} if storage file is not found. + * + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + @Override + Optional readEzFoodie() throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyEzFoodie} to the storage. + * + * @param ezFoodie cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + @Override + void saveEzFoodie(ReadOnlyEzFoodie ezFoodie) throws IOException; } diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java index 79868290974..81ee2e29037 100644 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ b/src/main/java/seedu/address/storage/StorageManager.java @@ -7,73 +7,166 @@ import seedu.address.commons.core.LogsCenter; import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyAccount; +import seedu.address.model.ReadOnlyEzFoodie; import seedu.address.model.ReadOnlyUserPrefs; import seedu.address.model.UserPrefs; /** - * Manages storage of AddressBook data in local storage. + * Manages storage of EzFoodie data in local storage. */ public class StorageManager implements Storage { private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; + private AccountStorage accountStorage; + private EzFoodieStorage ezFoodieStorage; private UserPrefsStorage userPrefsStorage; /** - * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}. + * Constructs a {@code StorageManager} with the given {@code AccountStorage}, {@code ezFoodieStorage} + * and {@code UserPrefStorage}. */ - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { + public StorageManager(AccountStorage accountStorage, EzFoodieStorage ezFoodieStorage, + UserPrefsStorage userPrefsStorage) { super(); - this.addressBookStorage = addressBookStorage; + this.accountStorage = accountStorage; + this.ezFoodieStorage = ezFoodieStorage; this.userPrefsStorage = userPrefsStorage; } // ================ UserPrefs methods ============================== + /** + * Returns the file path of the UserPrefs data file. + */ @Override public Path getUserPrefsFilePath() { return userPrefsStorage.getUserPrefsFilePath(); } + /** + * Returns UserPrefs data from storage. + * Returns {@code Optional.empty()} if storage file is not found. + * + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ @Override public Optional readUserPrefs() throws DataConversionException, IOException { return userPrefsStorage.readUserPrefs(); } + /** + * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage. + * + * @param userPrefs cannot be null. + * @throws IOException if there was any problem writing to the file. + */ @Override public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { userPrefsStorage.saveUserPrefs(userPrefs); } + // ================== Account methods =============================== - // ================ AddressBook methods ============================== + /** + * Returns the file path of the account file. + */ + @Override + public Path getAccountFilePath() { + return accountStorage.getAccountFilePath(); + } + /** + * Returns Account as a {@link ReadOnlyAccount}. + * Returns {@code Optional.empty()} if storage file is not found. + * + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + @Override + public Optional readAccount() throws DataConversionException, IOException { + return accountStorage.readAccount(); + } + + /** + * @see #getAccountFilePath() + */ @Override - public Path getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); + public Optional readAccount(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read account from file: " + filePath); + return accountStorage.readAccount(filePath); } + /** + * Saves the given {@link ReadOnlyAccount} to the storage. + * + * @param account cannot be null. + * @throws IOException if there was any problem writing to the file. + */ @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); + public void saveAccount(ReadOnlyAccount account) throws IOException { + accountStorage.saveAccount(account); } + /** + * @see #saveAccount(ReadOnlyAccount) + */ @Override - public Optional readAddressBook(Path filePath) throws DataConversionException, IOException { + public void saveAccount(ReadOnlyAccount account, Path filePath) throws IOException { + logger.fine("Attempting to write to account file: " + filePath); + accountStorage.saveAccount(account, filePath); + } + + // ================== EzFoodie methods =============================== + + /** + * Returns the file path of the data file. + */ + @Override + public Path getEzFoodieFilePath() { + return ezFoodieStorage.getEzFoodieFilePath(); + } + + /** + * Returns ezFoodie data as a {@link ReadOnlyEzFoodie}. + * Returns {@code Optional.empty()} if storage file is not found. + * + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + @Override + public Optional readEzFoodie() throws DataConversionException, IOException { + return readEzFoodie(ezFoodieStorage.getEzFoodieFilePath()); + } + + /** + * @see #getEzFoodieFilePath() + */ + @Override + public Optional readEzFoodie(Path filePath) throws DataConversionException, IOException { logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); + return ezFoodieStorage.readEzFoodie(filePath); } + /** + * Saves the given {@link ReadOnlyEzFoodie} to the storage. + * + * @param ezFoodie cannot be null. + * @throws IOException if there was any problem writing to the file. + */ @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); + public void saveEzFoodie(ReadOnlyEzFoodie ezFoodie) throws IOException { + saveEzFoodie(ezFoodie, ezFoodieStorage.getEzFoodieFilePath()); } + /** + * @see #saveEzFoodie(ReadOnlyEzFoodie) + */ @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { + public void saveEzFoodie(ReadOnlyEzFoodie ezFoodie, Path filePath) throws IOException { logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); + ezFoodieStorage.saveEzFoodie(ezFoodie, filePath); } } diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/seedu/address/storage/UserPrefsStorage.java index 29eef178dbc..1eb07ff2880 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/seedu/address/storage/UserPrefsStorage.java @@ -20,7 +20,8 @@ public interface UserPrefsStorage { /** * Returns UserPrefs data from storage. - * Returns {@code Optional.empty()} if storage file is not found. + * Returns {@code Optional.empty()} if storage file is not found. + * * @throws DataConversionException if the data in storage is not in the expected format. * @throws IOException if there was any problem when reading from the storage. */ @@ -28,6 +29,7 @@ public interface UserPrefsStorage { /** * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage. + * * @param userPrefs cannot be null. * @throws IOException if there was any problem writing to the file. */ diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/address/ui/CommandBox.java index 9e75478664b..263ed0a0d59 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/address/ui/CommandBox.java @@ -3,36 +3,61 @@ import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; import javafx.scene.layout.Region; +import seedu.address.commons.exceptions.PermissionException; +import seedu.address.commons.util.CommandUtil; import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; /** - * The UI component that is responsible for receiving user command inputs. + * Represents for the UI component that is responsible for receiving user command inputs. */ public class CommandBox extends UiPart { + /** + * Stands for error message for error style class. + */ public static final String ERROR_STYLE_CLASS = "error"; + + /** + * Uses FXML to identify CommandBox. + */ private static final String FXML = "CommandBox.fxml"; + /** + * Handles input commands. + */ private final CommandExecutor commandExecutor; + /** + * Represents text box in FXML. + */ @FXML private TextField commandTextField; /** - * Creates a {@code CommandBox} with the given {@code CommandExecutor}. + * Constructs a {@code CommandBox} with the given {@code CommandExecutor}. */ public CommandBox(CommandExecutor commandExecutor) { super(FXML); this.commandExecutor = commandExecutor; // calls #setStyleToDefault() whenever there is a change to the text of the command box. commandTextField.textProperty().addListener((unused1, unused2, unused3) -> setStyleToDefault()); + commandTextField.addEventHandler(KeyEvent.KEY_PRESSED, event -> { + if (event.getCode() == KeyCode.UP) { + commandTextField.setText(CommandUtil.getPreCommand()); + } + if (event.getCode() == KeyCode.DOWN) { + commandTextField.setText(CommandUtil.getNextCommand()); + } + }); } /** - * Handles the Enter button pressed event. + * Handles events triggered when command is entered. */ @FXML private void handleCommandEntered() { @@ -44,7 +69,7 @@ private void handleCommandEntered() { try { commandExecutor.execute(commandText); commandTextField.setText(""); - } catch (CommandException | ParseException e) { + } catch (CommandException | ParseException | PermissionException e) { setStyleToIndicateCommandFailure(); } } @@ -79,7 +104,7 @@ public interface CommandExecutor { * * @see seedu.address.logic.Logic#execute(String) */ - CommandResult execute(String commandText) throws CommandException, ParseException; + CommandResult execute(String commandText) throws CommandException, ParseException, PermissionException; } } diff --git a/src/main/java/seedu/address/ui/HelpBox.java b/src/main/java/seedu/address/ui/HelpBox.java new file mode 100644 index 00000000000..df069cb1eb8 --- /dev/null +++ b/src/main/java/seedu/address/ui/HelpBox.java @@ -0,0 +1,130 @@ +package seedu.address.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; +import javafx.scene.layout.Region; + +/** + * A UI component that displays information of commands. + */ +public class HelpBox extends UiPart { + + /** + * Stands for the official URL to our tP repository on GitHub. + */ + public static final String OFFICIAL_URL = + "https://ay2122s1-cs2103t-f12-4.github.io/tp/"; + + /** + * Uses FXML to identify HelpBox. + */ + private static final String FXML = "HelpBox.fxml"; + + /** + * Stands for components to be used in FXML. + * + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + @FXML + private Label login; + @FXML + private Label setPassword; + @FXML + private Label addNewMember; + @FXML + private Label deleteMemberByMemberId; + @FXML + private Label deleteMemberByIndexNumber; + @FXML + private Label findMemberByMemberId; + @FXML + private Label findMembersByName; + @FXML + private Label findMembersByPhone; + @FXML + private Label findMembersByEmail; + @FXML + private Label findMembersByRegistrationDate; + @FXML + private Label viewMemberProfile; + @FXML + private Label editMemberByMemberId; + @FXML + private Label editMemberByIndexNumber; + @FXML + private Label addTransactionByMemberId; + @FXML + private Label editTransactionByMemberIdTransactionId; + @FXML + private Label deleteTransactionByMemberIdTransactionId; + @FXML + private Label addReservationByMemberId; + @FXML + private Label editReservationByMemberIdTransactionId; + @FXML + private Label deleteReservationByMemberIdTransactionId; + @FXML + private Label sortMembersByCreditInAsc; + @FXML + private Label sortMembersByCreditInDesc; + @FXML + private Label redeemPointByMemberId; + @FXML + private Label redeemPointIndexNumber; + @FXML + private Label url; + @FXML + private Button copyButton; + + /** + * Creates a help box to show the commands. + */ + public HelpBox() { + super(FXML); + login.setText("login "); + setPassword.setText("set -pass/"); + addNewMember.setText("add -mem/ -n/ -p/ -e/ -a/
"); + deleteMemberByMemberId.setText("del -mem/ -id/"); + deleteMemberByIndexNumber.setText("del -mem/ -i/"); + findMemberByMemberId.setText("find -mem/ -id/"); + findMembersByName.setText("find -mem/ -n/"); + findMembersByPhone.setText("find -mem/ -p/"); + findMembersByEmail.setText("find -mem/ -e/"); + findMembersByRegistrationDate.setText("find -mem/ -d/"); + viewMemberProfile.setText("show -mem/ -id/"); + editMemberByMemberId.setText("edit -mem/ -id/ [-n/] [-p/] [-e/] [-a/
]"); + editMemberByIndexNumber.setText("edit -mem/ -i/ [-n/] [-p/] [-e/] [-a/
]"); + addTransactionByMemberId.setText("add -txn/ -b/ -id/"); + editTransactionByMemberIdTransactionId.setText( + "edit -txn/ -id/ -b/"); + deleteTransactionByMemberIdTransactionId.setText("del -txn/ -id/"); + addReservationByMemberId.setText("add -rs/ -dt/ -rm/ -id/"); + editReservationByMemberIdTransactionId.setText( + "edit -rs/ -id/ [-dt/] [-rm/]"); + deleteReservationByMemberIdTransactionId.setText("del -rs/ -id/"); + sortMembersByCreditInAsc.setText("sort -mem/ -c/ -a/"); + sortMembersByCreditInDesc.setText("sort -mem/ -c/ -d/"); + redeemPointByMemberId.setText("redeem -rd/ -id/"); + redeemPointIndexNumber.setText("redeem -rd/ -i/"); + url.setText(OFFICIAL_URL); + } + + /** + * Copies the URL to the user guide to the clipboard. + */ + @FXML + private void copyUrl() { + final Clipboard clipboard = Clipboard.getSystemClipboard(); + final ClipboardContent url = new ClipboardContent(); + url.putString(OFFICIAL_URL); + clipboard.setContent(url); + } +} diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java index 9a665915949..1285532f23c 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/address/ui/HelpWindow.java @@ -3,42 +3,53 @@ import java.util.logging.Logger; import javafx.fxml.FXML; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.input.Clipboard; -import javafx.scene.input.ClipboardContent; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.StackPane; import javafx.stage.Stage; import seedu.address.commons.core.LogsCenter; /** - * Controller for a help page + * Represents for Controlling a help page. */ public class HelpWindow extends UiPart { - public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html"; - public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; - - private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); + /** + * Uses FXML to identify HelpWindow. + */ private static final String FXML = "HelpWindow.fxml"; - @FXML - private Button copyButton; + /** + * Uses logger to log events happen in HelpWindow. + */ + private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); + /** + * Stands for components to be used in FXML. + */ @FXML - private Label helpMessage; + private StackPane helpBoxPlaceholder; /** - * Creates a new HelpWindow. + * Constructs a new {@code HelpWindow}. * * @param root Stage to use as the root of the HelpWindow. */ public HelpWindow(Stage root) { super(FXML, root); - helpMessage.setText(HELP_MESSAGE); + HelpBox helpBox = new HelpBox(); + helpBoxPlaceholder.getChildren().add(helpBox.getRoot()); + getRoot().addEventFilter(KeyEvent.KEY_PRESSED, event -> { + System.out.println(event.getCode()); + if (event.getCode() == KeyCode.ESCAPE) { + Stage stage = (Stage) getRoot().getScene().getWindow(); + stage.close(); + } + }); } /** - * Creates a new HelpWindow. + * Constructs a new {@code HelpWindow}. */ public HelpWindow() { this(new Stage()); @@ -46,7 +57,8 @@ public HelpWindow() { /** * Shows the help window. - * @throws IllegalStateException + * + * @throws IllegalStateException if the user input does not conform the expected format. *
    *
  • * if this method is called on a thread other than the JavaFX Application Thread. @@ -76,27 +88,18 @@ public boolean isShowing() { } /** - * Hides the help window. + * Hides the opened help window. */ public void hide() { getRoot().hide(); } /** - * Focuses on the help window. + * Focuses on current opened help window. */ public void focus() { getRoot().requestFocus(); } - /** - * Copies the URL to the user guide to the clipboard. - */ - @FXML - private void copyUrl() { - final Clipboard clipboard = Clipboard.getSystemClipboard(); - final ClipboardContent url = new ClipboardContent(); - url.putString(USERGUIDE_URL); - clipboard.setContent(url); - } + } diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 9106c3aa6e5..7f8e8458009 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -12,6 +12,7 @@ import javafx.stage.Stage; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.exceptions.PermissionException; import seedu.address.logic.Logic; import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; @@ -23,18 +24,36 @@ */ public class MainWindow extends UiPart { + /** + * Uses FXML to identify MainWindow. + */ private static final String FXML = "MainWindow.fxml"; + /** + * Uses logger to log events happen in MainWindow. + */ private final Logger logger = LogsCenter.getLogger(getClass()); + /** + * Stands for the main stage to contain all Ui parts in MainWindow. + */ private Stage primaryStage; + + /** + * Makes use of backend logic. + */ private Logic logic; // Independent Ui parts residing in this Ui container - private PersonListPanel personListPanel; + private MemberListPanel memberListPanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; + private MemberViewWindow memberViewWindow; + private SummaryWindow summaryWindow; + /** + * Stands for components to be used in FXML. + */ @FXML private StackPane commandBoxPlaceholder; @@ -42,16 +61,16 @@ public class MainWindow extends UiPart { private MenuItem helpMenuItem; @FXML - private StackPane personListPanelPlaceholder; + private StackPane memberListPanelPlaceholder; @FXML private StackPane resultDisplayPlaceholder; @FXML - private StackPane statusbarPlaceholder; + private StackPane statusBarPlaceholder; /** - * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}. + * Constructs a {@code MainWindow} with the given {@code Stage} and {@code Logic}. */ public MainWindow(Stage primaryStage, Logic logic) { super(FXML, primaryStage); @@ -66,19 +85,30 @@ public MainWindow(Stage primaryStage, Logic logic) { setAccelerators(); helpWindow = new HelpWindow(); + memberViewWindow = new MemberViewWindow(logic); + summaryWindow = new SummaryWindow(logic); } + /** + * Gets primary stage. + * + * @return Stage for the primary stage. + */ public Stage getPrimaryStage() { return primaryStage; } + /** + * Stands for the default accelerator setter for mainWindow. + */ private void setAccelerators() { setAccelerator(helpMenuItem, KeyCombination.valueOf("F1")); } /** * Sets the accelerator of a MenuItem. - * @param keyCombination the KeyCombination value of the accelerator + * + * @param keyCombination the KeyCombination value of the accelerator. */ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { menuItem.setAccelerator(keyCombination); @@ -110,14 +140,14 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { * Fills up all the placeholders of this window. */ void fillInnerParts() { - personListPanel = new PersonListPanel(logic.getFilteredPersonList()); - personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + memberListPanel = new MemberListPanel(logic.getUpdatedMemberList()); + memberListPanelPlaceholder.getChildren().add(memberListPanel.getRoot()); resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); - StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); - statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); + StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getEzFoodieFilePath()); + statusBarPlaceholder.getChildren().add(statusBarFooter.getRoot()); CommandBox commandBox = new CommandBox(this::executeCommand); commandBoxPlaceholder.getChildren().add(commandBox.getRoot()); @@ -147,6 +177,33 @@ public void handleHelp() { } } + /** + * Opens the member view window or focuses on it if it's already opened. + */ + @FXML + public void handleMemberView() { + if (!memberViewWindow.isShowing()) { + memberViewWindow.show(); + } else { + memberViewWindow.focus(); + } + } + + /** + * Opens the summary window or focuses on it if it's already opened. + */ + @FXML + public void handleSummary() { + if (!summaryWindow.isShowing()) { + summaryWindow.show(); + } else { + summaryWindow.focus(); + } + } + + /** + * Displays mainWindow to user. + */ void show() { primaryStage.show(); } @@ -160,11 +217,18 @@ private void handleExit() { (int) primaryStage.getX(), (int) primaryStage.getY()); logic.setGuiSettings(guiSettings); helpWindow.hide(); + memberViewWindow.hide(); + summaryWindow.hide(); primaryStage.hide(); } - public PersonListPanel getPersonListPanel() { - return personListPanel; + /** + * Gets all members in a list. + * + * @return memberListPanel component. + */ + public MemberListPanel getMemberListPanel() { + return memberListPanel; } /** @@ -172,7 +236,8 @@ public PersonListPanel getPersonListPanel() { * * @see seedu.address.logic.Logic#execute(String) */ - private CommandResult executeCommand(String commandText) throws CommandException, ParseException { + private CommandResult executeCommand(String commandText) + throws CommandException, ParseException, PermissionException { try { CommandResult commandResult = logic.execute(commandText); logger.info("Result: " + commandResult.getFeedbackToUser()); @@ -182,12 +247,20 @@ private CommandResult executeCommand(String commandText) throws CommandException handleHelp(); } + if (commandResult.isShowMemberView()) { + handleMemberView(); + } + + if (commandResult.isShowSummary()) { + handleSummary(); + } + if (commandResult.isExit()) { handleExit(); } return commandResult; - } catch (CommandException | ParseException e) { + } catch (CommandException | ParseException | PermissionException e) { logger.info("Invalid command: " + commandText); resultDisplay.setFeedbackToUser(e.getMessage()); throw e; diff --git a/src/main/java/seedu/address/ui/MemberCard.java b/src/main/java/seedu/address/ui/MemberCard.java new file mode 100644 index 00000000000..059e6234741 --- /dev/null +++ b/src/main/java/seedu/address/ui/MemberCard.java @@ -0,0 +1,107 @@ +package seedu.address.ui; + +import java.util.Comparator; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.commons.util.DateTimeUtil; +import seedu.address.model.member.Member; +import seedu.address.model.member.Tier; +import seedu.address.model.reservation.Reservation; + +/** + * A UI component that displays information of a {@code Member}. + */ +public class MemberCard extends UiPart { + + /** + * Uses FXML to identify MemberListCard. + */ + private static final String FXML = "MemberListCard.fxml"; + + /** + * Stands for current displaying member. + * + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + public final Member member; + + /** + * Stands for components to be used in FXML. + */ + @FXML + private HBox cardPane; + @FXML + private Label index; + @FXML + private Label id; + @FXML + private Label name; + @FXML + private Label phone; + @FXML + private Label email; + @FXML + private Label credit; + @FXML + private Label point; + @FXML + private Label tier; + @FXML + private Label reservation; + + /** + * Constructs a {@code MemberCode} with the given {@code Member} and index to display. + */ + public MemberCard(Member member, int displayedIndex) { + super(FXML); + this.member = member; + index.setText(displayedIndex + ". "); + id.setText(member.getId().value); + name.setText(member.getName().fullName); + phone.setText(member.getPhone().value); + email.setText(member.getEmail().value); + credit.setText(member.getCredit().value); + point.setText(member.getPoint().value); + tier.setText(Tier.getTierByCredit(Integer.parseInt(member.getCredit().value))); + tier.getStyleClass().add(Tier.getTierByCredit(Integer.parseInt(member.getCredit().value)).toLowerCase()); + member.getReservations().stream() + .sorted(Comparator.comparing(reservation -> DateTimeUtil + .parseDateTime(reservation.getDateTime().value))) + .filter(reservation -> Reservation.isValidDateTime(reservation.getDateTime())) + .findAny().ifPresentOrElse(comingReservation -> reservation.setText( + comingReservation.getDateTime().value + " " + + comingReservation.getRemark().value), () -> reservation.setText("")); + } + + /** + * Overrides equals by comparing their text and member objects. + * + * @param other object. + * @return true if equals, false otherwise. + */ + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof MemberCard)) { + return false; + } + + // state check + MemberCard card = (MemberCard) other; + return index.getText().equals(card.index.getText()) + && member.equals(card.member); + } +} diff --git a/src/main/java/seedu/address/ui/MemberDetailsCard.java b/src/main/java/seedu/address/ui/MemberDetailsCard.java new file mode 100644 index 00000000000..dce818d7d8c --- /dev/null +++ b/src/main/java/seedu/address/ui/MemberDetailsCard.java @@ -0,0 +1,116 @@ +package seedu.address.ui; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import seedu.address.commons.util.DateTimeUtil; +import seedu.address.model.member.Member; +import seedu.address.model.member.Tier; +import seedu.address.model.reservation.Reservation; +import seedu.address.model.transaction.Transaction; + +/** + * A UI component that displays information of a {@code Member}. + */ +public class MemberDetailsCard extends UiPart { + + /** + * Uses FXML to identify MemberDetailsCard. + */ + private static final String FXML = "MemberDetailsCard.fxml"; + + /** + * Stands for current displaying member. + */ + + public final Member member; + + /** + * Stands for Components to be used in FXML. + * + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + @FXML + private HBox cardPane; + @FXML + private Label id; + @FXML + private Label name; + @FXML + private Label phone; + @FXML + private Label email; + @FXML + private Label address; + @FXML + private Label registrationTime; + @FXML + private Label credit; + @FXML + private Label point; + @FXML + private Label tier; + @FXML + private StackPane reservationListPanelPlaceholder; + @FXML + private StackPane transactionListPanelPlaceholder; + + /** + * Creates a {@code MemberCode} with the given {@code members} to display. + */ + public MemberDetailsCard(Member member) { + super(FXML); + this.member = member; + id.setText(member.getId().value); + name.setText(member.getName().fullName); + phone.setText(member.getPhone().value); + email.setText(member.getEmail().value); + address.setText(member.getAddress().value); + registrationTime.setText(DateTimeUtil.timestampToDate(Long.parseLong(member.getTimestamp().value)).toString()); + credit.setText(member.getCredit().value); + point.setText(member.getPoint().value); + tier.setText(Tier.getTierByCredit(Integer.parseInt(member.getCredit().value))); + tier.getStyleClass().add(Tier.getTierByCredit(Integer.parseInt(member.getCredit().value)).toLowerCase()); + + ObservableList internalReservationList = FXCollections.observableArrayList(); + internalReservationList.addAll(member.getReservations()); + ReservationListPanel reservationListPanel = new ReservationListPanel(internalReservationList); + reservationListPanelPlaceholder.getChildren().add(reservationListPanel.getRoot()); + + ObservableList internalTransactionList = FXCollections.observableArrayList(); + internalTransactionList.addAll(member.getTransactions()); + TransactionListPanel transactionListPanel = new TransactionListPanel(internalTransactionList); + transactionListPanelPlaceholder.getChildren().add(transactionListPanel.getRoot()); + } + + /** + * Overrides equals by comparing their member objects. + * + * @param other object. + * @return true if contains the same member, false otherwise. + */ + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof MemberDetailsCard)) { + return false; + } + + // state check + MemberDetailsCard card = (MemberDetailsCard) other; + return member.equals(card.member); + } +} diff --git a/src/main/java/seedu/address/ui/MemberDetailsListPanel.java b/src/main/java/seedu/address/ui/MemberDetailsListPanel.java new file mode 100644 index 00000000000..bd6b0c1886e --- /dev/null +++ b/src/main/java/seedu/address/ui/MemberDetailsListPanel.java @@ -0,0 +1,52 @@ +package seedu.address.ui; + +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.model.member.Member; + +/** + * Represents for Panel containing the list of members. + */ +public class MemberDetailsListPanel extends UiPart { + + /** + * Uses FXML to identify MemberDetailsPanel. + */ + private static final String FXML = "MemberDetailsListPanel.fxml"; + + /** + * Stands for components to be used in FXML. + */ + @FXML + private ListView memberDetailsListView; + + /** + * Creates a {@code MemberListPanel} with the given {@code ObservableList}. + */ + public MemberDetailsListPanel(ObservableList memberList) { + super(FXML); + memberDetailsListView.setItems(memberList); + memberDetailsListView.setCellFactory(listView -> new MemberDetailsListViewCell()); + } + + /** + * Customs {@code ListCell} that displays the graphics of a {@code Member} using a {@code MemberDetailsCard}. + */ + class MemberDetailsListViewCell extends ListCell { + @Override + protected void updateItem(Member member, boolean empty) { + super.updateItem(member, empty); + + if (empty || member == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new MemberDetailsCard(member).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/MemberListPanel.java b/src/main/java/seedu/address/ui/MemberListPanel.java new file mode 100644 index 00000000000..e7388555f59 --- /dev/null +++ b/src/main/java/seedu/address/ui/MemberListPanel.java @@ -0,0 +1,52 @@ +package seedu.address.ui; + +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.model.member.Member; + +/** + * Represents for Panel containing the list of members. + */ +public class MemberListPanel extends UiPart { + + /** + * Uses FXML to identify MemberListPanel. + */ + private static final String FXML = "MemberListPanel.fxml"; + + /** + * Stands for components to be used in FXML. + */ + @FXML + private ListView memberListView; + + /** + * Creates a {@code MemberListPanel} with the given {@code ObservableList}. + */ + public MemberListPanel(ObservableList memberList) { + super(FXML); + memberListView.setItems(memberList); + memberListView.setCellFactory(listView -> new MemberListViewCell()); + } + + /** + * Customs {@code ListCell} that displays the graphics of a {@code Member} using a {@code MemberCard}. + */ + class MemberListViewCell extends ListCell { + @Override + protected void updateItem(Member member, boolean empty) { + super.updateItem(member, empty); + + if (empty || member == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new MemberCard(member, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/MemberViewWindow.java b/src/main/java/seedu/address/ui/MemberViewWindow.java new file mode 100644 index 00000000000..64050fde29f --- /dev/null +++ b/src/main/java/seedu/address/ui/MemberViewWindow.java @@ -0,0 +1,105 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import javafx.fxml.FXML; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.Logic; + +/** + * Controller for a member view page for related details. + */ +public class MemberViewWindow extends UiPart { + + /** + * Stands for logger to log events happen in MemberViewWindow. + */ + private static final Logger logger = LogsCenter.getLogger(MemberViewWindow.class); + + /** + * Uses FXML to identify MemberViewWindow. + */ + private static final String FXML = "MemberViewWindow.fxml"; + + @FXML + private StackPane memberDetailsListPlaceholder; + + /** + * Constructs a new {@code MemberViewWindow} . + * + * @param root Stage to use as the root of the MemberViewWindow. + * @param logic summary of logic. + */ + public MemberViewWindow(Stage root, Logic logic) { + super(FXML, root); + MemberDetailsListPanel memberDetailsListPanel = new MemberDetailsListPanel(logic.getUpdatedMemberListForView()); + memberDetailsListPlaceholder.getChildren().add(memberDetailsListPanel.getRoot()); + getRoot().addEventFilter(KeyEvent.KEY_PRESSED, event -> { + System.out.println(event.getCode()); + if (event.getCode() == KeyCode.ESCAPE) { + Stage stage = (Stage) getRoot().getScene().getWindow(); + stage.close(); + } + }); + } + + /** + * Constructs a new {@code MemberViewWindow}, with a default new stage. + * + * @param logic logic refers to backend logic. + */ + public MemberViewWindow(Logic logic) { + this(new Stage(), logic); + } + + /** + * Shows the MemberView window. + * + * @throws IllegalStateException if the user input does not conform the expected format. + *
      + *
    • + * if this method is called on a thread other than the JavaFX Application Thread. + *
    • + *
    • + * if this method is called during animation or layout processing. + *
    • + *
    • + * if this method is called on the primary stage. + *
    • + *
    • + * if {@code dialogStage} is already showing. + *
    • + *
    + */ + public void show() { + logger.fine("Showing member view page."); + getRoot().show(); + getRoot().centerOnScreen(); + } + + /** + * Returns true if the MemberView window is currently being shown. + */ + public boolean isShowing() { + return getRoot().isShowing(); + } + + /** + * Hides current opened MemberView window. + */ + public void hide() { + getRoot().hide(); + } + + /** + * Focuses on current opened MemberView window. + */ + public void focus() { + getRoot().requestFocus(); + } + +} diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java deleted file mode 100644 index 7fc927bc5d9..00000000000 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ /dev/null @@ -1,77 +0,0 @@ -package seedu.address.ui; - -import java.util.Comparator; - -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.layout.FlowPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Region; -import seedu.address.model.person.Person; - -/** - * An UI component that displays information of a {@code Person}. - */ -public class PersonCard extends UiPart { - - private static final String FXML = "PersonListCard.fxml"; - - /** - * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. - * As a consequence, UI elements' variable names cannot be set to such keywords - * or an exception will be thrown by JavaFX during runtime. - * - * @see The issue on AddressBook level 4 - */ - - public final Person person; - - @FXML - private HBox cardPane; - @FXML - private Label name; - @FXML - private Label id; - @FXML - private Label phone; - @FXML - private Label address; - @FXML - private Label email; - @FXML - private FlowPane tags; - - /** - * Creates a {@code PersonCode} with the given {@code Person} and index to display. - */ - public PersonCard(Person person, int displayedIndex) { - super(FXML); - this.person = person; - id.setText(displayedIndex + ". "); - name.setText(person.getName().fullName); - phone.setText(person.getPhone().value); - address.setText(person.getAddress().value); - email.setText(person.getEmail().value); - person.getTags().stream() - .sorted(Comparator.comparing(tag -> tag.tagName)) - .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof PersonCard)) { - return false; - } - - // state check - PersonCard card = (PersonCard) other; - return id.getText().equals(card.id.getText()) - && person.equals(card.person); - } -} diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java deleted file mode 100644 index f4c501a897b..00000000000 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ /dev/null @@ -1,49 +0,0 @@ -package seedu.address.ui; - -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.layout.Region; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; - -/** - * Panel containing the list of persons. - */ -public class PersonListPanel extends UiPart { - private static final String FXML = "PersonListPanel.fxml"; - private final Logger logger = LogsCenter.getLogger(PersonListPanel.class); - - @FXML - private ListView personListView; - - /** - * Creates a {@code PersonListPanel} with the given {@code ObservableList}. - */ - public PersonListPanel(ObservableList personList) { - super(FXML); - personListView.setItems(personList); - personListView.setCellFactory(listView -> new PersonListViewCell()); - } - - /** - * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}. - */ - class PersonListViewCell extends ListCell { - @Override - protected void updateItem(Person person, boolean empty) { - super.updateItem(person, empty); - - if (empty || person == null) { - setGraphic(null); - setText(null); - } else { - setGraphic(new PersonCard(person, getIndex() + 1).getRoot()); - } - } - } - -} diff --git a/src/main/java/seedu/address/ui/ReservationCard.java b/src/main/java/seedu/address/ui/ReservationCard.java new file mode 100644 index 00000000000..0505130f6d0 --- /dev/null +++ b/src/main/java/seedu/address/ui/ReservationCard.java @@ -0,0 +1,73 @@ +package seedu.address.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.model.reservation.Reservation; + +/** + * A UI component that displays information of a {@code Reservation}. + */ +public class ReservationCard extends UiPart { + + /** + * Uses FXML to identify ReservationListCard. + */ + private static final String FXML = "ReservationListCard.fxml"; + + /** + * Stands for current displaying reservation. + */ + public final Reservation reservation; + + /** + * Stands for components to be used in FXML. + * + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + @FXML + private HBox cardPane; + @FXML + private Label id; + @FXML + private Label dateTime; + @FXML + private Label remark; + + /** + * Constructs a {@code ReservationCode} with the given {@code Reservation} to display. + */ + public ReservationCard(Reservation reservation) { + super(FXML); + this.reservation = reservation; + id.setText(reservation.getId().value); + dateTime.setText(reservation.getDateTime().value); + remark.setText(reservation.getRemark().value); + } + + /** + * Overrides equals by comparing reservation objects. + */ + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ReservationCard)) { + return false; + } + + // state check + ReservationCard card = (ReservationCard) other; + return reservation.equals(card.reservation); + } +} diff --git a/src/main/java/seedu/address/ui/ReservationListPanel.java b/src/main/java/seedu/address/ui/ReservationListPanel.java new file mode 100644 index 00000000000..5902ef0d4a8 --- /dev/null +++ b/src/main/java/seedu/address/ui/ReservationListPanel.java @@ -0,0 +1,65 @@ +package seedu.address.ui; + +import java.time.LocalDateTime; + +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.util.DateTimeUtil; +import seedu.address.model.reservation.Reservation; + +/** + * Represents for Panel containing the list of reservations. + */ +public class ReservationListPanel extends UiPart { + + /** + * Uses FXML to identify ReservationListPanel. + */ + private static final String FXML = "ReservationListPanel.fxml"; + + /** + * Stands for components to be used in FXML. + */ + @FXML + private ListView reservationListView; + + /** + * Creates a {@code ReservationListPanel} with the given {@code ObservableList}. + */ + public ReservationListPanel(ObservableList reservationList) { + super(FXML); + reservationListView.setItems(reservationList.sorted((r1, r2) -> { + LocalDateTime dateTimeR1 = DateTimeUtil.parseDateTime(r1.getDateTime().value); + LocalDateTime dateTimeR2 = DateTimeUtil.parseDateTime(r2.getDateTime().value); + if (dateTimeR1.isAfter(dateTimeR2)) { + return -1; + } else if (dateTimeR1.isEqual(dateTimeR2)) { + return 0; + } else { + return 1; + } + })); + reservationListView.setCellFactory(listView -> new ReservationListViewCell()); + } + + /** + * Customs {@code ListCell} that displays the graphics of a {@code Reservation} using a {@code ReservationCard}. + */ + class ReservationListViewCell extends ListCell { + @Override + protected void updateItem(Reservation reservation, boolean empty) { + super.updateItem(reservation, empty); + + if (empty || reservation == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new ReservationCard(reservation).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/address/ui/ResultDisplay.java index 7d98e84eedf..418d506ee2d 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/seedu/address/ui/ResultDisplay.java @@ -7,19 +7,33 @@ import javafx.scene.layout.Region; /** - * A ui for the status bar that is displayed at the header of the application. + * A UI for the status bar that is displayed at the header of the application. */ public class ResultDisplay extends UiPart { + /** + * Uses FXML to identify ResultDisplay. + */ private static final String FXML = "ResultDisplay.fxml"; + /** + * Stands for components to be used in FXML. + */ @FXML private TextArea resultDisplay; + /** + * Constructs a {@code ResultDisplay}. + */ public ResultDisplay() { super(FXML); } + /** + * Sets feedback to User with given message to display {@code feedbackToUser}. + * + * @param feedbackToUser message to display + */ public void setFeedbackToUser(String feedbackToUser) { requireNonNull(feedbackToUser); resultDisplay.setText(feedbackToUser); diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/address/ui/StatusBarFooter.java index b577f829423..58c486346cd 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/address/ui/StatusBarFooter.java @@ -4,25 +4,42 @@ import java.nio.file.Paths; import javafx.fxml.FXML; +import javafx.geometry.HPos; import javafx.scene.control.Label; +import javafx.scene.layout.GridPane; import javafx.scene.layout.Region; +import seedu.address.commons.status.LoginStatus; /** * A ui for the status bar that is displayed at the footer of the application. */ public class StatusBarFooter extends UiPart { + /** + * Uses FXML to identify StatusBarFooter. + */ private static final String FXML = "StatusBarFooter.fxml"; + /** + * Stands for components to be used in FXML. + */ @FXML private Label saveLocationStatus; + @FXML + private Label loginStatus; + + /** - * Creates a {@code StatusBarFooter} with the given {@code Path}. + * Creates a StatusBarFooter to display. + * + * @param saveLocation current location of saved file. */ public StatusBarFooter(Path saveLocation) { super(FXML); saveLocationStatus.setText(Paths.get(".").resolve(saveLocation).toString()); + loginStatus.textProperty().bind(LoginStatus.CURRENT_STATUS); + GridPane.setHalignment(loginStatus, HPos.RIGHT); } } diff --git a/src/main/java/seedu/address/ui/SummaryBox.java b/src/main/java/seedu/address/ui/SummaryBox.java new file mode 100644 index 00000000000..91cf19d643e --- /dev/null +++ b/src/main/java/seedu/address/ui/SummaryBox.java @@ -0,0 +1,112 @@ +package seedu.address.ui; + +import java.text.DecimalFormat; + +import javafx.beans.binding.Bindings; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.Region; +import seedu.address.model.member.Member; +import seedu.address.model.transaction.Transaction; + +/** + * A UI component that displays information of a summary. + */ +public class SummaryBox extends UiPart { + + /** + * Uses FXML to identify SummaryBox. + */ + private static final String FXML = "SummaryBox.fxml"; + + /** + * Stands for pattern used to format decimals. + */ + private static final String PATTERN = "#.##"; + + /** + * Stands for current displaying memberList. + */ + public final ObservableList memberList; + + /** + * Stands for components to be used in FXML. + * + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + @FXML + private Label totalNumberOfMembers; + @FXML + private Label totalNumberOfTransactions; + @FXML + private Label totalAmountOfTransactions; + @FXML + private Label totalNumberOfTransactionsInPastMonth; + @FXML + private Label totalAmountOfTransactionsInPastMonth; + @FXML + private Label totalNumberOfTransactionsInPast3Months; + @FXML + private Label totalAmountOfTransactionsInPast3Months; + @FXML + private Label totalNumberOfTransactionsInPast6Months; + @FXML + private Label totalAmountOfTransactionsInPast6Months; + + /** + * Creates a {@code MemberCode} with the given {@code Member} and index to display. + */ + public SummaryBox(ObservableList memberList) { + super(FXML); + this.memberList = memberList; + DecimalFormat df = new DecimalFormat(PATTERN); + totalNumberOfMembers.textProperty().bind( + Bindings.createStringBinding(() -> String.valueOf(memberList.size()), memberList)); + totalNumberOfTransactions.textProperty().bind( + Bindings.createStringBinding(() -> String.valueOf(memberList.stream() + .mapToInt(member -> member.getTransactions().size()).sum()), memberList)); + totalAmountOfTransactions.textProperty().bind( + Bindings.createStringBinding(() -> df.format(memberList.stream() + .mapToDouble(member -> member.getTransactions().stream() + .mapToDouble(transaction -> transaction.getBilling() + .getDoubleValue()).sum()).sum()), memberList)); + totalNumberOfTransactionsInPastMonth.textProperty().bind( + Bindings.createStringBinding(() -> String.valueOf(memberList.stream() + .mapToLong(member -> member.getTransactions().stream().filter(transaction -> Transaction + .isWithinPastMonths(transaction.getTimestamp().getLongValue(), 1)) + .count()).sum()), memberList)); + totalAmountOfTransactionsInPastMonth.textProperty().bind( + Bindings.createStringBinding(() -> df.format(memberList.stream() + .mapToDouble(member -> member.getTransactions().stream().filter(transaction -> Transaction + .isWithinPastMonths(transaction.getTimestamp().getLongValue(), 1)) + .mapToDouble(transaction -> transaction.getBilling() + .getDoubleValue()).sum()).sum()), memberList)); + totalNumberOfTransactionsInPast3Months.textProperty().bind( + Bindings.createStringBinding(() -> String.valueOf(memberList.stream() + .mapToLong(member -> member.getTransactions().stream().filter(transaction -> Transaction + .isWithinPastMonths(transaction.getTimestamp().getLongValue(), 3)) + .count()).sum()), memberList)); + totalAmountOfTransactionsInPast3Months.textProperty().bind( + Bindings.createStringBinding(() -> df.format(memberList.stream() + .mapToDouble(member -> member.getTransactions().stream().filter(transaction -> Transaction + .isWithinPastMonths(transaction.getTimestamp().getLongValue(), 3)) + .mapToDouble(transaction -> transaction.getBilling() + .getDoubleValue()).sum()).sum()), memberList)); + totalNumberOfTransactionsInPast6Months.textProperty().bind( + Bindings.createStringBinding(() -> String.valueOf(memberList.stream() + .mapToLong(member -> member.getTransactions().stream().filter(transaction -> Transaction + .isWithinPastMonths(transaction.getTimestamp().getLongValue(), 6)) + .count()).sum()), memberList)); + totalAmountOfTransactionsInPast6Months.textProperty().bind( + Bindings.createStringBinding(() -> df.format(memberList.stream() + .mapToDouble(member -> member.getTransactions().stream().filter(transaction -> Transaction + .isWithinPastMonths(transaction.getTimestamp().getLongValue(), 6)) + .mapToDouble(transaction -> transaction.getBilling() + .getDoubleValue()).sum()).sum()), memberList)); + } +} diff --git a/src/main/java/seedu/address/ui/SummaryWindow.java b/src/main/java/seedu/address/ui/SummaryWindow.java new file mode 100644 index 00000000000..0e3bb2cb9e6 --- /dev/null +++ b/src/main/java/seedu/address/ui/SummaryWindow.java @@ -0,0 +1,108 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import javafx.fxml.FXML; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; +import seedu.address.commons.core.LogsCenter; +import seedu.address.logic.Logic; + +/** + * Controller for a summary page. + */ +public class SummaryWindow extends UiPart { + + /** + * Uses FXML to identify SummaryWindow. + */ + private static final String FXML = "SummaryWindow.fxml"; + + /** + * Stands for logger to log events happened in SummaryWindow. + */ + private static final Logger logger = LogsCenter.getLogger(SummaryWindow.class); + + /** + * Stands for components to be used in FXML. + */ + @FXML + private StackPane summaryBoxPlaceholder; + + /** + * Constructs a new {@code SummaryWindow} . + * + * @param root Stage to use as the root of the SummaryWindow. + * @param logic summary of logic. + */ + public SummaryWindow(Stage root, Logic logic) { + super(FXML, root); + SummaryBox summaryBox = new SummaryBox(logic.getUpdatedMemberList()); + summaryBoxPlaceholder.getChildren().add(summaryBox.getRoot()); + getRoot().addEventFilter(KeyEvent.KEY_PRESSED, event -> { + System.out.println(event.getCode()); + if (event.getCode() == KeyCode.ESCAPE) { + Stage stage = (Stage) getRoot().getScene().getWindow(); + stage.close(); + } + }); + } + + + /** + * Constructs a SummaryWindow with a default new stage. + * + * @param logic logic refers to backend logic. + */ + public SummaryWindow(Logic logic) { + this(new Stage(), logic); + } + + /** + * Shows the summary window. + * + * @throws IllegalStateException if the user input does not conform the expected format. + *
      + *
    • + * if this method is called on a thread other than the JavaFX Application Thread. + *
    • + *
    • + * if this method is called during animation or layout processing. + *
    • + *
    • + * if this method is called on the primary stage. + *
    • + *
    • + * if {@code dialogStage} is already showing. + *
    • + *
    + */ + public void show() { + logger.fine("Showing summary page."); + getRoot().show(); + getRoot().centerOnScreen(); + } + + /** + * Returns true if the summary window is currently being shown. + */ + public boolean isShowing() { + return getRoot().isShowing(); + } + + /** + * Hides current opened summary window. + */ + public void hide() { + getRoot().hide(); + } + + /** + * Focuses on current opened summary window. + */ + public void focus() { + getRoot().requestFocus(); + } +} diff --git a/src/main/java/seedu/address/ui/TransactionCard.java b/src/main/java/seedu/address/ui/TransactionCard.java new file mode 100644 index 00000000000..977692902bd --- /dev/null +++ b/src/main/java/seedu/address/ui/TransactionCard.java @@ -0,0 +1,76 @@ +package seedu.address.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.commons.util.DateTimeUtil; +import seedu.address.model.transaction.Transaction; + +/** + * A UI component that displays information of a {@code Transaction}. + */ +public class TransactionCard extends UiPart { + + /** + * Uses FXML to identify TransactionListCard. + */ + private static final String FXML = "TransactionListCard.fxml"; + + /** + * Stands for current displaying transaction. + */ + public final Transaction transaction; + + /** + * Stands for components to be used in FXML. + * + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + @FXML + private HBox cardPane; + @FXML + private Label id; + @FXML + private Label dateTime; + @FXML + private Label billing; + + + /** + * Constructs a TransactionCard with input transaction object. + * + * @param transaction including transaction details to be displayed. + */ + public TransactionCard(Transaction transaction) { + super(FXML); + this.transaction = transaction; + id.setText(transaction.getId().value); + dateTime.setText(DateTimeUtil.timestampToDate(Long.parseLong(transaction.getTimestamp().value)).toString()); + billing.setText(transaction.getBilling().value); + } + + /** + * Overrides equals by comparing transaction objects. + */ + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof TransactionCard)) { + return false; + } + + // state check + TransactionCard card = (TransactionCard) other; + return transaction.equals(card.transaction); + } +} diff --git a/src/main/java/seedu/address/ui/TransactionListPanel.java b/src/main/java/seedu/address/ui/TransactionListPanel.java new file mode 100644 index 00000000000..33d50203991 --- /dev/null +++ b/src/main/java/seedu/address/ui/TransactionListPanel.java @@ -0,0 +1,54 @@ +package seedu.address.ui; + +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.model.transaction.Transaction; + +/** + * Represents for Panel containing the list of transactions. + */ +public class TransactionListPanel extends UiPart { + + /** + * Uses FXML to identify TransactionListPanel. + */ + private static final String FXML = "TransactionListPanel.fxml"; + + /** + * Stands for components to be used in FXML. + */ + @FXML + private ListView transactionListView; + + /** + * Constructs a TransactionListPanel with input transactionList of a specific member. + * + * @param transactionList refers to all transactions of a specific member. + */ + public TransactionListPanel(ObservableList transactionList) { + super(FXML); + transactionListView.setItems(transactionList); + transactionListView.setCellFactory(listView -> new TransactionListViewCell()); + } + + /** + * Customs {@code ListCell} that displays the graphics of a {@code Transaction} using a {@code TransactionCard}. + */ + class TransactionListViewCell extends ListCell { + @Override + protected void updateItem(Transaction transaction, boolean empty) { + super.updateItem(transaction, empty); + + if (empty || transaction == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new TransactionCard(transaction).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/seedu/address/ui/Ui.java index 17aa0b494fe..6a88886747d 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/seedu/address/ui/Ui.java @@ -3,7 +3,7 @@ import javafx.stage.Stage; /** - * API of UI component + * API of UI component. */ public interface Ui { diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/address/ui/UiManager.java index 882027e4537..d18f89b47d3 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/address/ui/UiManager.java @@ -17,12 +17,29 @@ */ public class UiManager implements Ui { + /** + * Stands for message to be shown about alert dialog pane. + */ public static final String ALERT_DIALOG_PANE_FIELD_ID = "alertDialogPane"; + /** + * Stands for logger to log events happened in UiManager. + */ private static final Logger logger = LogsCenter.getLogger(UiManager.class); - private static final String ICON_APPLICATION = "/images/address_book_32.png"; + /** + * Locates ezFoodie Logo. + */ + private static final String ICON_APPLICATION = "/images/ezfoodie_icon.png"; + + /** + * Refers to backend logic. + */ private Logic logic; + + /** + * Refers to the window to be shown when user first opens ezFoodie. + */ private MainWindow mainWindow; /** @@ -33,6 +50,9 @@ public UiManager(Logic logic) { this.logic = logic; } + /** + * Starts the UI (and the Application). + */ @Override public void start(Stage primaryStage) { logger.info("Starting UI..."); @@ -51,17 +71,36 @@ public void start(Stage primaryStage) { } } + /** + * Gets ezFoodie logo. + * + * @param imagePath path of the images stored. + * @return Image object of ezFoodie logo. + */ private Image getImage(String imagePath) { return new Image(MainApp.class.getResourceAsStream(imagePath)); } + /** + * Alerts user by showing alert dialog. + * + * @param type alert type. + * @param title tile of alert. + * @param headerText header text to show. + * @param contentText content text to show. + */ void showAlertDialogAndWait(Alert.AlertType type, String title, String headerText, String contentText) { showAlertDialogAndWait(mainWindow.getPrimaryStage(), type, title, headerText, contentText); } /** - * Shows an alert dialog on {@code owner} with the given parameters. - * This method only returns after the user has closed the alert dialog. + * Returns after the user has closed the alert dialog. + * + * @param owner stage. + * @param type alert type. + * @param title title to show. + * @param headerText header text to show. + * @param contentText content text to show. */ private static void showAlertDialogAndWait(Stage owner, AlertType type, String title, String headerText, String contentText) { diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/address/ui/UiPart.java index fc820e01a9c..f3359bd1bd7 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/address/ui/UiPart.java @@ -14,7 +14,9 @@ */ public abstract class UiPart { - /** Resource folder where FXML files are stored. */ + /** + * Resource folder where FXML files are stored. + */ public static final String FXML_FILE_FOLDER = "/view/"; private final FXMLLoader fxmlLoader = new FXMLLoader(); @@ -60,6 +62,7 @@ public T getRoot() { /** * Loads the object hierarchy from a FXML document. + * * @param location Location of the FXML document. * @param root Specifies the root of the object hierarchy. */ diff --git a/src/main/resources/images/ezFoodie_icon.png b/src/main/resources/images/ezFoodie_icon.png new file mode 100644 index 00000000000..9dc264e19b0 Binary files /dev/null and b/src/main/resources/images/ezFoodie_icon.png differ diff --git a/src/main/resources/images/member_icon.png b/src/main/resources/images/member_icon.png new file mode 100644 index 00000000000..2b18c53f3bd Binary files /dev/null and b/src/main/resources/images/member_icon.png differ diff --git a/src/main/resources/images/summary_icon.png b/src/main/resources/images/summary_icon.png new file mode 100644 index 00000000000..b27ba5b4997 Binary files /dev/null and b/src/main/resources/images/summary_icon.png differ diff --git a/src/main/resources/view/CommandBox.fxml b/src/main/resources/view/CommandBox.fxml index 09f6d6fe9e4..7b791985d54 100644 --- a/src/main/resources/view/CommandBox.fxml +++ b/src/main/resources/view/CommandBox.fxml @@ -6,4 +6,3 @@ - diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 9ce9bcfb569..284fcdfe671 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -122,16 +122,61 @@ .cell_big_label { -fx-font-family: "Open Sans Semibold"; - -fx-font-size: 16px; + -fx-font-size: 18px; -fx-text-fill: #010504; } .cell_small_label { -fx-font-family: "Open Sans Regular"; - -fx-font-size: 13px; + -fx-font-size: 15px; -fx-text-fill: #010504; } +.cell_label_width_80 { + -fx-pref-width: 80px; +} + +.cell_label_width_90 { + -fx-pref-width: 90px; +} + +.cell_label_width_100 { + -fx-pref-width: 100px; +} + +.cell_label_width_290 { + -fx-pref-width: 290px; +} + +.cell_label_width_298 { + -fx-pref-width: 298px; +} + +.cell_label_width_300 { + -fx-pref-width: 300px; +} + +.cell_label_width_400 { + -fx-pref-width: 400px; +} + +.cell_label_width_500 { + -fx-pref-width: 500px; +} + +.cell_label_width_600 { + -fx-pref-width: 600px; +} + +.cell_label_width_700 { + -fx-pref-width: 700px; +} + +.cell_label_left_right_border_1 { + -fx-border-color: white; + -fx-border-width: 0 1 0 1; +} + .stack-pane { -fx-background-color: derive(#1d1d1d, 20%); } @@ -328,7 +373,7 @@ -fx-text-fill: white; } -#filterField, #personListPanel, #personWebpage { +#filterField, #memberListPanel, #memberWebpage { -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); } @@ -348,5 +393,49 @@ -fx-padding: 1 3 1 3; -fx-border-radius: 2; -fx-background-radius: 2; - -fx-font-size: 11; + -fx-font-size: 14; +} + +#tier { + -fx-text-fill: black; + -fx-padding: 1 3 1 3; + -fx-border-radius: 2; + -fx-background-radius: 2; + -fx-font-size: 14; +} + +.bronze { + -fx-background-color: #cd7f32; +} + +.silver { + -fx-background-color: #b2b2b2; +} + +.gold { + -fx-background-color: #ffd700; +} + +.platinum { + -fx-background-color: #f2f2f2; +} + +.split_line { + strokeWidth=3.0 +} + +#summaryBox .label { + -fx-text-fill: white; +} + +#reservationList .label { + -fx-text-fill: white; +} + +#transactionList .label { + -fx-text-fill: white; +} + +#helpBox .label { + -fx-text-fill: white; } diff --git a/src/main/resources/view/HelpBox.fxml b/src/main/resources/view/HelpBox.fxml new file mode 100644 index 00000000000..ca848f6025d --- /dev/null +++ b/src/main/resources/view/HelpBox.fxml @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index 32bcf2c8e70..91925b29bc1 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -6,15 +6,14 @@ - + title="ezFoodie" minWidth="450" minHeight="600" onCloseRequest="#handleExit"> - + @@ -47,14 +46,14 @@ - + - + - + diff --git a/src/main/resources/view/MemberDetailsCard.fxml b/src/main/resources/view/MemberDetailsCard.fxml new file mode 100644 index 00000000000..55b4c40f56d --- /dev/null +++ b/src/main/resources/view/MemberDetailsCard.fxml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MemberDetailsListPanel.fxml b/src/main/resources/view/MemberDetailsListPanel.fxml new file mode 100644 index 00000000000..2a273098e9c --- /dev/null +++ b/src/main/resources/view/MemberDetailsListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/view/MemberListCard.fxml b/src/main/resources/view/MemberListCard.fxml new file mode 100644 index 00000000000..f17f5b59e47 --- /dev/null +++ b/src/main/resources/view/MemberListCard.fxml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/MemberListPanel.fxml similarity index 77% rename from src/main/resources/view/PersonListPanel.fxml rename to src/main/resources/view/MemberListPanel.fxml index 8836d323cc5..e8580b322be 100644 --- a/src/main/resources/view/PersonListPanel.fxml +++ b/src/main/resources/view/MemberListPanel.fxml @@ -4,5 +4,5 @@ - + diff --git a/src/main/resources/view/MemberViewWindow.fxml b/src/main/resources/view/MemberViewWindow.fxml new file mode 100644 index 00000000000..333b20f9880 --- /dev/null +++ b/src/main/resources/view/MemberViewWindow.fxml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml deleted file mode 100644 index f08ea32ad55..00000000000 --- a/src/main/resources/view/PersonListCard.fxml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/view/ReservationListCard.fxml b/src/main/resources/view/ReservationListCard.fxml new file mode 100644 index 00000000000..6169f923698 --- /dev/null +++ b/src/main/resources/view/ReservationListCard.fxml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/ReservationListPanel.fxml b/src/main/resources/view/ReservationListPanel.fxml new file mode 100644 index 00000000000..15e775a79b4 --- /dev/null +++ b/src/main/resources/view/ReservationListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/view/StatusBarFooter.fxml b/src/main/resources/view/StatusBarFooter.fxml index 149f62bd29c..48ce633f289 100644 --- a/src/main/resources/view/StatusBarFooter.fxml +++ b/src/main/resources/view/StatusBarFooter.fxml @@ -9,4 +9,5 @@